Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
nettedexposurecalculator.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 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
23#include <ql/time/date.hpp>
24#include <ql/time/calendars/weekendsonly.hpp>
25
26using namespace std;
27using namespace QuantLib;
28
29namespace ore {
30namespace analytics {
31
33 const QuantLib::ext::shared_ptr<Portfolio>& portfolio, const QuantLib::ext::shared_ptr<Market>& market,
34 const QuantLib::ext::shared_ptr<NPVCube>& cube, const string& baseCurrency, const string& configuration,
35 const Real quantile, const CollateralExposureHelper::CalculationType calcType, const bool multiPath,
36 const QuantLib::ext::shared_ptr<NettingSetManager>& nettingSetManager,
37 const QuantLib::ext::shared_ptr<CollateralBalances>& collateralBalances,
38 const map<string, vector<vector<Real>>>& nettingSetDefaultValue,
39 const map<string, vector<vector<Real>>>& nettingSetCloseOutValue,
40 const map<string, vector<vector<Real>>>& nettingSetMporPositiveFlow,
41 const map<string, vector<vector<Real>>>& nettingSetMporNegativeFlow,
42 const QuantLib::ext::shared_ptr<AggregationScenarioData>& scenarioData,
43 const QuantLib::ext::shared_ptr<CubeInterpretation> cubeInterpretation, const bool applyInitialMargin,
44 const QuantLib::ext::shared_ptr<DynamicInitialMarginCalculator>& dimCalculator, const bool fullInitialCollateralisation,
45 const bool marginalAllocation, const Real marginalAllocationLimit,
46 const QuantLib::ext::shared_ptr<NPVCube>& tradeExposureCube, const Size allocatedEpeIndex, const Size allocatedEneIndex,
47 const bool flipViewXVA, const bool withMporStickyDate, const MporCashFlowMode mporCashFlowMode)
48 : portfolio_(portfolio), market_(market), cube_(cube), baseCurrency_(baseCurrency), configuration_(configuration),
49 quantile_(quantile), calcType_(calcType), multiPath_(multiPath), nettingSetManager_(nettingSetManager),
50 collateralBalances_(collateralBalances),
51 nettingSetDefaultValue_(nettingSetDefaultValue), nettingSetCloseOutValue_(nettingSetCloseOutValue),
52 nettingSetMporPositiveFlow_(nettingSetMporPositiveFlow), nettingSetMporNegativeFlow_(nettingSetMporNegativeFlow),
53 scenarioData_(scenarioData), cubeInterpretation_(cubeInterpretation), applyInitialMargin_(applyInitialMargin),
54 dimCalculator_(dimCalculator), fullInitialCollateralisation_(fullInitialCollateralisation),
55 marginalAllocation_(marginalAllocation), marginalAllocationLimit_(marginalAllocationLimit),
56 tradeExposureCube_(tradeExposureCube), allocatedEpeIndex_(allocatedEpeIndex),
57 allocatedEneIndex_(allocatedEneIndex), flipViewXVA_(flipViewXVA), withMporStickyDate_(withMporStickyDate),
58 mporCashFlowMode_(mporCashFlowMode) {
59
60 set<string> nettingSetIds;
61 for (auto nettingSet : nettingSetDefaultValue) {
62 nettingSetIds.insert(nettingSet.first);
63 if (flipViewXVA_) {
64 if (nettingSetManager_->get(nettingSet.first)->activeCsaFlag()) {
65 nettingSetManager_->get(nettingSet.first)->csaDetails()->invertCSA();
66 }
67 }
68 }
69
70 nettedCube_= QuantLib::ext::make_shared<SinglePrecisionInMemoryCube>(
71 market_->asofDate(), nettingSetIds, cube->dates(),
72 cube->samples()); // Exposure after collateral
73 if (multiPath) {
74 exposureCube_ = QuantLib::ext::make_shared<SinglePrecisionInMemoryCubeN>(
75 market_->asofDate(), nettingSetIds, cube->dates(),
76 cube->samples(), EXPOSURE_CUBE_DEPTH); // EPE, ENE
77 } else {
78 exposureCube_ = QuantLib::ext::make_shared<DoublePrecisionInMemoryCubeN>(
79 market_->asofDate(), nettingSetIds, cube->dates(),
80 1, EXPOSURE_CUBE_DEPTH); // EPE, ENE
81 }
82};
83
85 LOG("Compute netting set exposure profiles");
86
87 const Date today = market_->asofDate();
88 const DayCounter dc = ActualActual(ActualActual::ISDA);
89
90 vector<Real> times = vector<Real>(cube_->dates().size(), 0.0);
91 for (Size i = 0; i < cube_->dates().size(); i++)
92 times[i] = dc.yearFraction(today, cube_->dates()[i]);
93
94 map<string, Real> nettingSetValueToday;
95 map<string, Date> nettingSetMaturity;
96 map<string, Size> nettingSetSize;
97 Size cubeIndex = 0;
98 for (auto tradeIt = portfolio_->trades().begin(); tradeIt != portfolio_->trades().end(); ++tradeIt, ++cubeIndex) {
99 const auto& trade = tradeIt->second;
100 string tradeId = tradeIt->first;
101 string nettingSetId = trade->envelope().nettingSetId();
102 string cp = trade->envelope().counterparty();
103 if (counterpartyMap_.find(nettingSetId) == counterpartyMap_.end())
104 counterpartyMap_[nettingSetId] = trade->envelope().counterparty();
105 else {
106 QL_REQUIRE(counterpartyMap_[nettingSetId] == cp, "counterparty name is not unique within the netting set");
107 }
108 Real npv;
109 if (flipViewXVA_) {
110 npv = -cube_->getT0(cubeIndex);
111 } else {
112 npv = cube_->getT0(cubeIndex);
113 }
114
115 if (nettingSetValueToday.find(nettingSetId) == nettingSetValueToday.end()) {
116 nettingSetValueToday[nettingSetId] = 0.0;
117 nettingSetMaturity[nettingSetId] = today;
118 nettingSetSize[nettingSetId] = 0;
119 }
120
121 nettingSetValueToday[nettingSetId] += npv;
122
123 if (trade->maturity() > nettingSetMaturity[nettingSetId])
124 nettingSetMaturity[nettingSetId] = trade->maturity();
125 nettingSetSize[nettingSetId]++;
126 }
127
128 vector<vector<Real>> averagePositiveAllocation(portfolio_->size(), vector<Real>(cube_->dates().size(), 0.0));
129 vector<vector<Real>> averageNegativeAllocation(portfolio_->size(), vector<Real>(cube_->dates().size(), 0.0));
130
131 Size nettingSetCount = 0;
132 for (auto n : nettingSetDefaultValue_) {
133 string nettingSetId = n.first;
134 vector<vector<Real>> data = n.second;
135 QuantLib::ext::shared_ptr<NettingSetDefinition> netting = nettingSetManager_->get(nettingSetId);
136
137 // retrieve collateral balances object, if possible
138 QuantLib::ext::shared_ptr<CollateralBalance> balance = nullptr;
139 if (collateralBalances_ && collateralBalances_->has(nettingSetId)) {
140 balance = collateralBalances_->get(nettingSetId);
141 DLOG("got collateral balances for netting set " << nettingSetId);
142 }
143
144 //only for active CSA and calcType == NoLag close-out value is relevant
145 if (netting->activeCsaFlag() && calcType_ == CollateralExposureHelper::CalculationType::NoLag)
146 data = nettingSetCloseOutValue_[nettingSetId];
147
148 vector<vector<Real>> nettingSetMporPositiveFlow = nettingSetMporPositiveFlow_[nettingSetId];
149 vector<vector<Real>> nettingSetMporNegativeFlow = nettingSetMporNegativeFlow_[nettingSetId];
150
151 LOG("Aggregate exposure for netting set " << nettingSetId);
152 // Get the collateral account balance paths for the netting set.
153 // The pointer may remain empty if there is no CSA or if it is inactive.
154 QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>> collateral =
155 collateralPaths(nettingSetId,
156 nettingSetValueToday[nettingSetId],
157 nettingSetDefaultValue_[nettingSetId],
158 nettingSetMaturity[nettingSetId]);
159
160 // Get the CSA index for Eonia Floor calculation below
161 colva_[nettingSetId] = 0.0;
162 collateralFloor_[nettingSetId] = 0.0;
163 string csaIndexName;
164 Handle<IborIndex> csaIndex;
165 bool applyInitialMargin = false;
166 CSA::Type initialMarginType = CSA::Bilateral;
167 if (netting->activeCsaFlag()) {
168 csaIndexName = netting->csaDetails()->index();
169 if (csaIndexName != "") {
170 csaIndex = market_->iborIndex(csaIndexName);
171 QL_REQUIRE(scenarioData_->has(AggregationScenarioDataType::IndexFixing, csaIndexName),
172 "scenario data does not provide index values for " << csaIndexName);
173 }
174 QL_REQUIRE(netting->csaDetails(), "active CSA for netting set " << nettingSetId
175 << ", but CSA details not initialised");
176 applyInitialMargin = netting->csaDetails()->applyInitialMargin() && applyInitialMargin_;
177 initialMarginType = netting->csaDetails()->initialMarginType();
178 LOG("ApplyInitialMargin=" << applyInitialMargin << " for netting set " << nettingSetId
179 << ", CSA IM=" << netting->csaDetails()->applyInitialMargin()
180 << ", CSA IM Type=" << initialMarginType
181 << ", Analytics DIM=" << applyInitialMargin_);
182 if (applyInitialMargin_ && !netting->csaDetails()->applyInitialMargin())
183 ALOG("ApplyInitialMargin deactivated at netting set level " << nettingSetId);
184 if (!applyInitialMargin_ && netting->csaDetails()->applyInitialMargin())
185 ALOG("ApplyInitialMargin deactivated in analytics, but active at netting set level " << nettingSetId);
186 }
187
188 // Retrieve the constant independent amount from the CSA data and the VM balance
189 // This is used below to reduce the exposure across all paths and time steps.
190 // See below for the conversion to base currency.
191 Real initialVM = 0, initialVMbase = 0;
192 Real initialIM = 0, initialIMbase = 0;
193 string csaCurrency = "";
194 if (netting->activeCsaFlag() && balance) {
195 initialVM = balance->variationMargin();
196 initialIM = balance->initialMargin();
197 double fx = 1.0;
198 if (baseCurrency_ != balance->currency())
199 fx = market_->fxSpot(balance->currency() + baseCurrency_)->value();
200 initialVMbase = fx * initialVM;
201 initialIMbase = fx * initialIM;
202 DLOG("Netting set " << nettingSetId << ", initial VM: " << initialVMbase << " " << baseCurrency_);
203 DLOG("Netting set " << nettingSetId << ", initial IM: " << initialIMbase << " " << baseCurrency_);
204 }
205 else {
206 DLOG("Netting set " << nettingSetId << ", IA base = VM base = 0");
207 }
208
209 Handle<YieldTermStructure> curve = market_->discountCurve(baseCurrency_, configuration_);
210 vector<Real> epe(cube_->dates().size() + 1, 0.0);
211 vector<Real> ene(cube_->dates().size() + 1, 0.0);
212 vector<Real> ee_b(cube_->dates().size() + 1, 0.0);
213 vector<Real> eee_b(cube_->dates().size() + 1, 0.0);
214 vector<Real> eee_b_kva_1(cube_->dates().size() + 1, 0.0);
215 vector<Real> eee_b_kva_2(cube_->dates().size() + 1, 0.0);
216 vector<Real> eepe_b_kva_1(cube_->dates().size() + 1, 0.0);
217 vector<Real> eepe_b_kva_2(cube_->dates().size() + 1, 0.0);
218 vector<Real> eab(cube_->dates().size() + 1, 0.0);
219 vector<Real> pfe(cube_->dates().size() + 1, 0.0);
220 vector<Real> colvaInc(cube_->dates().size() + 1, 0.0);
221 vector<Real> eoniaFloorInc(cube_->dates().size() + 1, 0.0);
222 Real npv = nettingSetValueToday[nettingSetId];
223 if ((fullInitialCollateralisation_) & (netting->activeCsaFlag())) {
224 // This assumes that the collateral at t=0 is the same as the npv at t=0.
225 epe[0] = 0;
226 ene[0] = 0;
227 pfe[0] = 0;
228 } else {
229 epe[0] = std::max(npv - initialVMbase - initialIMbase, 0.0);
230 ene[0] = std::max(-npv + initialVMbase, 0.0);
231 pfe[0] = std::max(npv - initialVMbase - initialIMbase, 0.0);
232 }
233 // The fullInitialCollateralisation flag doesn't affect the eab, which feeds into the "ExpectedCollateral"
234 // column of the 'exposure_nettingset_*' reports. We always assume the full collateral here.
235 eab[0] = npv;
236 ee_b[0] = epe[0];
237 eee_b[0] = ee_b[0];
238 nettedCube_->setT0(npv, nettingSetCount);
239 exposureCube_->setT0(epe[0], nettingSetCount, ExposureIndex::EPE);
240 exposureCube_->setT0(ene[0], nettingSetCount, ExposureIndex::ENE);
241
242 for (Size j = 0; j < cube_->dates().size(); ++j) {
243
244 Date date = cube_->dates()[j];
245 Date prevDate = j > 0 ? cube_->dates()[j - 1] : today;
246 vector<Real> distribution(cube_->samples(), 0.0);
247 for (Size k = 0; k < cube_->samples(); ++k) {
248 Real balance = 0.0;
249 if (collateral) {
250 balance = collateral->at(k)->accountBalance(date);
251 if (netting->csaDetails()->csaCurrency() != baseCurrency_) {
252 // Convert from CSACurrency to baseCurrency
253 double fxRate = scenarioData_->get(j, k, AggregationScenarioDataType::FXSpot,
254 netting->csaDetails()->csaCurrency());
255 balance *= fxRate;
256 }
257 }
258
259 eab[j + 1] += balance / cube_->samples();
260
261 Real mporCashFlow = 0;
262 // If ActualDate is active, then the cash flows over mpor can be configured.
263 // Otherwise (StickyDate is active), it is assumed that no cash flow over mpor is paid out.
264 if (!withMporStickyDate_) {
265 if (mporCashFlowMode_ == MporCashFlowMode::BothPay) {
266 // in cube generation -actual date- the (+/-) cashflows over mpor are
267 // payed out, i.e. are not part of the exposure .
268 mporCashFlow = 0;
269 } else if (mporCashFlowMode_ == MporCashFlowMode::NonePay) {
270 // +/- cashflows is to be incorporated in the exposure
271 mporCashFlow = (nettingSetMporPositiveFlow[j][k] + nettingSetMporNegativeFlow[j][k]);
272 } else if (mporCashFlowMode_ ==
273 MporCashFlowMode::WePay) {
274 // only positive cash flows (i.e. cp's cashflows) is to be
275 // incorporated in the exposure, since cp does not pay out cash
276 // flows
277 mporCashFlow = nettingSetMporPositiveFlow[j][k];
278 } else if (mporCashFlowMode_ ==
279 MporCashFlowMode::TheyPay) { // onyl negative cash flows (i.e. our cashflows) is to be
280 // incorporated in the exposure, ince we do not pay out cash
281 // flows
282 mporCashFlow = nettingSetMporNegativeFlow[j][k];
283 }
284 }
285 Real exposure = data[j][k] - balance + mporCashFlow;
286 Real dim = 0.0;
287 if (applyInitialMargin && collateral) { // don't apply initial margin without VM, i.e. inactive CSA
288 // Initial Margin
289 // Use IM to reduce exposure
290 // Size dimIndex = j == 0 ? 0 : j - 1;
291 Size dimIndex = j;
292 dim = dimCalculator_->dynamicIM(nettingSetId)[dimIndex][k];
293 QL_REQUIRE(dim >= 0, "negative DIM for set " << nettingSetId << ", date " << j << ", sample " << k
294 << ": " << dim);
295 }
296 Real dim_epe = 0;
297 Real dim_ene = 0;
298 if (initialMarginType != CSA::Type::PostOnly)
299 dim_epe = dim;
300 if (initialMarginType != CSA::Type::CallOnly)
301 dim_ene = dim;
302
303 // dim here represents the held IM, and is expressed as a positive number
304 epe[j + 1] += std::max(exposure - dim_epe, 0.0) / cube_->samples();
305 // dim here represents the posted IM, and is expressed as a positive number
306 ene[j + 1] += std::max(-exposure - dim_ene, 0.0) / cube_->samples();
307 distribution[k] = exposure - dim_epe;
308 nettedCube_->set(exposure, nettingSetCount, j, k);
309
310 Real epeIncrement = std::max(exposure - dim_epe, 0.0) / cube_->samples();
311 DLOG("sample " << k << " date " << j << fixed << showpos << setprecision(2)
312 << ": VM " << setw(15) << balance
313 << ": NPV " << setw(15) << data[j][k]
314 << ": NPV-C " << setw(15) << distribution[k]
315 << ": EPE " << setw(15) << epeIncrement);
316
317 if (multiPath_) {
318 exposureCube_->set(std::max(exposure - dim_epe, 0.0), nettingSetCount, j, k, ExposureIndex::EPE);
319 exposureCube_->set(std::max(-exposure - dim_ene, 0.0), nettingSetCount, j, k, ExposureIndex::ENE);
320 }
321
322 if (netting->activeCsaFlag()) {
323 Real indexValue = 0.0;
324 DayCounter dc = ActualActual(ActualActual::ISDA);
325 if (csaIndexName != "") {
326 indexValue = scenarioData_->get(j, k, AggregationScenarioDataType::IndexFixing, csaIndexName);
327 dc = csaIndex->dayCounter();
328 }
329 Real dcf = dc.yearFraction(prevDate, date);
330 Real collateralSpread = (balance >= 0.0 ? netting->csaDetails()->collatSpreadRcv() : netting->csaDetails()->collatSpreadPay());
331 Real numeraire = scenarioData_->get(j, k, AggregationScenarioDataType::Numeraire);
332 Real colvaDelta = -balance * collateralSpread * dcf / numeraire / cube_->samples();
333 // intuitive floorDelta including collateralSpread would be:
334 // -balance * (max(indexValue - collateralSpread,0) - (indexValue - collateralSpread)) * dcf /
335 // samples
336 Real floorDelta = -balance * std::max(-(indexValue - collateralSpread), 0.0) * dcf / numeraire / cube_->samples();
337 colvaInc[j + 1] += colvaDelta;
338 colva_[nettingSetId] += colvaDelta;
339 eoniaFloorInc[j + 1] += floorDelta;
340 collateralFloor_[nettingSetId] += floorDelta;
341 }
342
344 Size i = 0;
345 for (auto tradeIt = portfolio_->trades().begin(); tradeIt != portfolio_->trades().end();
346 ++tradeIt, ++i) {
347 const auto& trade = tradeIt->second;
348 string nid = trade->envelope().nettingSetId();
349 if (nid != nettingSetId)
350 continue;
351
352 Real allocation = 0.0;
353 if (balance == 0.0)
354 allocation = cubeInterpretation_->getDefaultNpv(cube_, i, j, k);
355 // else if (data[j][k] == 0.0)
356 else if (fabs(data[j][k]) <= marginalAllocationLimit_)
357 allocation = exposure / nettingSetSize[nid];
358 else
359 allocation = exposure * cubeInterpretation_->getDefaultNpv(cube_, i, j, k) / data[j][k];
360
361 if (multiPath_) {
362 if (exposure > 0.0)
363 tradeExposureCube_->set(allocation, i, j, k, allocatedEpeIndex_);
364 else
365 tradeExposureCube_->set(-allocation, i, j, k, allocatedEneIndex_);
366 } else {
367 if (exposure > 0.0)
368 averagePositiveAllocation[i][j] += allocation / cube_->samples();
369 else
370 averageNegativeAllocation[i][j] -= allocation / cube_->samples();
371 }
372 }
373 }
374 }
375 if (!multiPath_) {
376 exposureCube_->set(epe[j + 1], nettingSetCount, j, 0, ExposureIndex::EPE);
377 exposureCube_->set(ene[j + 1], nettingSetCount, j, 0, ExposureIndex::ENE);
378 }
379 ee_b[j + 1] = epe[j + 1] / curve->discount(cube_->dates()[j]);
380 eee_b[j + 1] = std::max(eee_b[j], ee_b[j + 1]);
381 std::sort(distribution.begin(), distribution.end());
382 Size index = Size(floor(quantile_ * (cube_->samples() - 1) + 0.5));
383 pfe[j + 1] = std::max(distribution[index], 0.0);
384 }
385 ee_b_[nettingSetId] = ee_b;
386 eee_b_[nettingSetId] = eee_b;
387 pfe_[nettingSetId] = pfe;
388 expectedCollateral_[nettingSetId] = eab;
389 colvaInc_[nettingSetId] = colvaInc;
390 eoniaFloorInc_[nettingSetId] = eoniaFloorInc;
391
392 nettingSetCount++;
393
394 Real epe_b = 0;
395 Real eepe_b = 0;
396
397 Size t = 0;
398 Calendar cal = WeekendsOnly();
399 Date maturity = std::min(cal.adjust(today + 1 * Years + 4 * Days), nettingSetMaturity[nettingSetId]);
400 QuantLib::Real maturityTime = dc.yearFraction(today, maturity);
401
402 while (t < cube_->dates().size() && times[t] <= maturityTime)
403 ++t;
404
405 if (t > 0) {
406 vector<double> weights(t);
407 weights[0] = times[0];
408 for (Size k = 1; k < t; k++)
409 weights[k] = times[k] - times[k - 1];
410 double totalWeights = std::accumulate(weights.begin(), weights.end(), 0.0);
411 for (Size k = 0; k < t; k++)
412 weights[k] /= totalWeights;
413
414 for (Size k = 0; k < t; k++) {
415 epe_b += ee_b[k] * weights[k];
416 eepe_b += eee_b[k] * weights[k];
417 }
418 }
419 epe_b_[nettingSetId] = epe_b;
420 eepe_b_[nettingSetId] = eepe_b;
421 }
422
424 for (Size i = 0; i < portfolio_->trades().size(); ++i) {
425 for (Size j = 0; j < cube_->dates().size(); ++j) {
426 tradeExposureCube_->set(averagePositiveAllocation[i][j], i, j, 0, allocatedEpeIndex_);
427 tradeExposureCube_->set(averageNegativeAllocation[i][j], i, j, 0, allocatedEneIndex_);
428 }
429 }
430 }
431}
432
433QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>>
435 const string& nettingSetId,
436 const Real& nettingSetValueToday,
437 const vector<vector<Real>>& nettingSetValue,
438 const Date& nettingSetMaturity) {
439
440 QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>> collateral;
441
442 if (!nettingSetManager_->has(nettingSetId) || !nettingSetManager_->get(nettingSetId)->activeCsaFlag()) {
443 LOG("CSA missing or inactive for netting set " << nettingSetId);
444 return collateral;
445 }
446
447 // retrieve collateral balances object, if possible
448 QuantLib::ext::shared_ptr<CollateralBalance> balance = nullptr;
449 if (collateralBalances_ && collateralBalances_->has(nettingSetId)) {
450 balance = collateralBalances_->get(nettingSetId);
451 LOG("got collateral balances for netting set " << nettingSetId);
452 }
453
454 LOG("Build collateral account balance paths for netting set " << nettingSetId);
455 QuantLib::ext::shared_ptr<NettingSetDefinition> netting = nettingSetManager_->get(nettingSetId);
456 string csaFxPair = netting->csaDetails()->csaCurrency() + baseCurrency_;
457 Real csaFxRateToday = 1.0;
458 if (netting->csaDetails()->csaCurrency() != baseCurrency_)
459 csaFxRateToday = market_->fxRate(csaFxPair, configuration_)->value();
460 LOG("CSA FX rate for pair " << csaFxPair << " = " << csaFxRateToday);
461
462 // Don't use Settings::instance().evaluationDate() here, this has moved to simulation end date.
463 Date today = market_->asofDate();
464 string csaIndexName = netting->csaDetails()->index();
465 // avoid thrown errors of the index fixing here on holidays of the index, instead take the preceding date then.
466 if (!market_->iborIndex(csaIndexName, configuration_)->isValidFixingDate(today)) {
467 today = market_->iborIndex(csaIndexName, configuration_)->fixingCalendar().adjust(today, Preceding);
468 }
469 Real csaRateToday = market_->iborIndex(csaIndexName, configuration_)->fixing(today);
470 LOG("CSA compounding rate for index " << csaIndexName << " = " << setprecision(8) << csaRateToday << " as of " << today);
471
472 // Copy scenario data to keep the collateral exposure helper unchanged
473 vector<vector<Real>> csaScenFxRates(cube_->dates().size(), vector<Real>(cube_->samples(), 0.0));
474 vector<vector<Real>> csaScenRates(cube_->dates().size(), vector<Real>(cube_->samples(), 0.0));
475 if (netting->csaDetails()->csaCurrency() != baseCurrency_) {
476 QL_REQUIRE(scenarioData_->has(AggregationScenarioDataType::FXSpot, netting->csaDetails()->csaCurrency()),
477 "scenario data does not provide FX rates for " << csaFxPair);
478 }
479 if (csaIndexName != "") {
480 QL_REQUIRE(scenarioData_->has(AggregationScenarioDataType::IndexFixing, csaIndexName),
481 "scenario data does not provide index values for " << csaIndexName);
482 }
483 for (Size j = 0; j < cube_->dates().size(); ++j) {
484 for (Size k = 0; k < cube_->samples(); ++k) {
485 if (netting->csaDetails()->csaCurrency() != baseCurrency_)
486 csaScenFxRates[j][k] = cubeInterpretation_->getDefaultAggregationScenarioData(
487 AggregationScenarioDataType::FXSpot, j, k, netting->csaDetails()->csaCurrency());
488 else
489 csaScenFxRates[j][k] = 1.0;
490 if (csaIndexName != "") {
491 csaScenRates[j][k] = cubeInterpretation_->getDefaultAggregationScenarioData(
492 AggregationScenarioDataType::IndexFixing, j, k, csaIndexName);
493 }
494 }
495 }
496
498 netting, // this netting set's definition
499 nettingSetValueToday, // today's netting set NPV
500 market_->asofDate(), // original evaluation date
501 nettingSetValue, // matrix of netting set values by date and sample
502 nettingSetMaturity, // netting set's maximum maturity date
503 cube_->dates(), // vector of future evaluation dates
504 csaFxRateToday, // today's FX rate for CSA to base currency, possibly 1
505 csaScenFxRates, // matrix of fx rates by date and sample, possibly 1
506 csaRateToday, // today's collateral compounding rate in CSA currency
507 csaScenRates, // matrix of CSA ccy short rates by date and sample
508 calcType_,
509 balance); // initial collateral balances (VM, IM, IA) for the netting set
510 LOG("Collateral account balance paths for netting set " << nettingSetId << " done");
511
512 return collateral;
513}
514
515vector<Real> NettedExposureCalculator::getMeanExposure(const string& tid, ExposureIndex index) {
516 vector<Real> exp(cube_->dates().size() + 1, 0.0);
517 exp[0] = exposureCube_->getT0(tid, index);
518 for (Size i = 0; i < cube_->dates().size(); i++) {
519 if (multiPath_) {
520 for (Size k = 0; k < exposureCube_->samples(); k++) {
521 exp[i + 1] += exposureCube_->get(tid, cube_->dates()[i], k, index);
522 }
523 exp[i + 1] /= exposureCube_->samples();
524 }
525 else {
526 exp[i + 1] = exposureCube_->get(tid, cube_->dates()[i], 0, index);
527 }
528 }
529 return exp;
530}
531
532const string& NettedExposureCalculator::counterparty(const string nettingSetId) {
533 auto it = counterpartyMap_.find(nettingSetId);
534 QL_REQUIRE(it != counterpartyMap_.end(),
535 "counterparty not found for netting set id " << nettingSetId);
536 return it->second;
537}
538
539} // namespace analytics
540} // namespace ore
static QuantLib::ext::shared_ptr< vector< QuantLib::ext::shared_ptr< CollateralAccount > > > collateralBalancePaths(const QuantLib::ext::shared_ptr< NettingSetDefinition > &csaDef, const Real &nettingSetPv, const Date &date_t0, const vector< vector< Real > > &nettingSetValues, const Date &nettingSet_maturity, const vector< Date > &dateGrid, const Real &csaFxTodayRate, const vector< vector< Real > > &csaFxScenarioRates, const Real &csaTodayCollatCurve, const vector< vector< Real > > &csaScenCollatCurves, const CalculationType &calcType=Symmetric, const QuantLib::ext::shared_ptr< CollateralBalance > &balance=QuantLib::ext::shared_ptr< CollateralBalance >())
QuantLib::ext::shared_ptr< Market > market_
map< string, vector< vector< Real > > > nettingSetDefaultValue()
QuantLib::ext::shared_ptr< NPVCube > tradeExposureCube_
map< string, vector< vector< Real > > > nettingSetDefaultValue_
map< string, vector< vector< Real > > > nettingSetMporPositiveFlow_
const QuantLib::ext::shared_ptr< DynamicInitialMarginCalculator > dimCalculator_
const QuantLib::ext::shared_ptr< CollateralBalances > collateralBalances_
vector< Real > getMeanExposure(const string &tid, ExposureIndex index)
QuantLib::ext::shared_ptr< NPVCube > nettedCube_
vector< Real > & eee_b(const string &nid)
QuantLib::ext::shared_ptr< NPVCube > exposureCube_
vector< Real > & pfe(const string &nid)
virtual void build()
Compute exposures along all paths and fill result structures.
NettedExposureCalculator(const QuantLib::ext::shared_ptr< Portfolio > &portfolio, const QuantLib::ext::shared_ptr< Market > &market, const QuantLib::ext::shared_ptr< NPVCube > &cube, const string &baseCurrency, const string &configuration, const Real quantile, const CollateralExposureHelper::CalculationType calcType, const bool multiPath, const QuantLib::ext::shared_ptr< NettingSetManager > &nettingSetManager, const QuantLib::ext::shared_ptr< CollateralBalances > &collateralBalances, const map< string, vector< vector< Real > > > &nettingSetDefaultValue, const map< string, vector< vector< Real > > > &nettingSetCloseOutValue, const map< string, vector< vector< Real > > > &nettingSetMporPositiveFlow, const map< string, vector< vector< Real > > > &nettingSetMporNegativeFlow, const QuantLib::ext::shared_ptr< AggregationScenarioData > &scenarioData, const QuantLib::ext::shared_ptr< CubeInterpretation > cubeInterpretation, const bool applyInitialMargin, const QuantLib::ext::shared_ptr< DynamicInitialMarginCalculator > &dimCalculator, const bool fullInitialCollateralisation, const bool marginalAllocation, const Real marginalAllocationLimit, const QuantLib::ext::shared_ptr< NPVCube > &tradeExposureCube, const Size allocatedEpeIndex, const Size allocatedEneIndex, const bool flipViewXVA, const bool withMporStickyDate, const MporCashFlowMode mporCashFlowMode)
map< string, vector< vector< Real > > > nettingSetMporPositiveFlow()
QuantLib::ext::shared_ptr< Portfolio > portfolio_
const QuantLib::ext::shared_ptr< AggregationScenarioData > scenarioData_
map< string, vector< vector< Real > > > nettingSetMporNegativeFlow_
const string & counterparty(const string nettingSetId)
QuantLib::ext::shared_ptr< CubeInterpretation > cubeInterpretation_
map< string, std::vector< Real > > eoniaFloorInc_
map< string, vector< vector< Real > > > nettingSetCloseOutValue_
CollateralExposureHelper::CalculationType calcType_
map< string, std::vector< Real > > colvaInc_
QuantLib::ext::shared_ptr< vector< QuantLib::ext::shared_ptr< CollateralAccount > > > collateralPaths(const string &nettingSetId, const Real &nettingSetValueToday, const vector< vector< Real > > &nettingSetValue, const Date &nettingSetMaturity)
vector< Real > & ee_b(const string &nid)
map< string, std::vector< Real > > expectedCollateral_
map< string, vector< vector< Real > > > nettingSetMporNegativeFlow()
const QuantLib::ext::shared_ptr< NettingSetManager > nettingSetManager_
QuantLib::ext::shared_ptr< NPVCube > cube_
data
#define LOG(text)
#define DLOG(text)
#define ALOG(text)
Time maturity
RandomVariable exp(RandomVariable x)
Size size(const ValueType &v)
Exposure calculator.