23#include <ql/time/date.hpp>
24#include <ql/time/calendars/weekendsonly.hpp>
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,
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) {
60 set<string> nettingSetIds;
62 nettingSetIds.insert(nettingSet.first);
70 nettedCube_= QuantLib::ext::make_shared<SinglePrecisionInMemoryCube>(
71 market_->asofDate(), nettingSetIds, cube->dates(),
74 exposureCube_ = QuantLib::ext::make_shared<SinglePrecisionInMemoryCubeN>(
75 market_->asofDate(), nettingSetIds, cube->dates(),
78 exposureCube_ = QuantLib::ext::make_shared<DoublePrecisionInMemoryCubeN>(
79 market_->asofDate(), nettingSetIds, cube->dates(),
85 LOG(
"Compute netting set exposure profiles");
87 const Date today =
market_->asofDate();
88 const DayCounter dc = ActualActual(ActualActual::ISDA);
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]);
94 map<string, Real> nettingSetValueToday;
95 map<string, Date> nettingSetMaturity;
96 map<string, Size> nettingSetSize;
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();
106 QL_REQUIRE(
counterpartyMap_[nettingSetId] == cp,
"counterparty name is not unique within the netting set");
110 npv = -
cube_->getT0(cubeIndex);
112 npv =
cube_->getT0(cubeIndex);
115 if (nettingSetValueToday.find(nettingSetId) == nettingSetValueToday.end()) {
116 nettingSetValueToday[nettingSetId] = 0.0;
117 nettingSetMaturity[nettingSetId] = today;
118 nettingSetSize[nettingSetId] = 0;
121 nettingSetValueToday[nettingSetId] += npv;
123 if (trade->maturity() > nettingSetMaturity[nettingSetId])
124 nettingSetMaturity[nettingSetId] = trade->maturity();
125 nettingSetSize[nettingSetId]++;
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));
131 Size nettingSetCount = 0;
133 string nettingSetId = n.first;
134 vector<vector<Real>>
data = n.second;
135 QuantLib::ext::shared_ptr<NettingSetDefinition> netting =
nettingSetManager_->get(nettingSetId);
138 QuantLib::ext::shared_ptr<CollateralBalance> balance =
nullptr;
141 DLOG(
"got collateral balances for netting set " << nettingSetId);
151 LOG(
"Aggregate exposure for netting set " << nettingSetId);
154 QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>> collateral =
156 nettingSetValueToday[nettingSetId],
158 nettingSetMaturity[nettingSetId]);
161 colva_[nettingSetId] = 0.0;
164 Handle<IborIndex> csaIndex;
165 bool applyInitialMargin =
false;
167 if (netting->activeCsaFlag()) {
168 csaIndexName = netting->csaDetails()->index();
169 if (csaIndexName !=
"") {
170 csaIndex =
market_->iborIndex(csaIndexName);
172 "scenario data does not provide index values for " << csaIndexName);
174 QL_REQUIRE(netting->csaDetails(),
"active CSA for netting set " << nettingSetId
175 <<
", but CSA details not initialised");
177 initialMarginType = netting->csaDetails()->initialMarginType();
178 LOG(
"ApplyInitialMargin=" << applyInitialMargin <<
" for netting set " << nettingSetId
179 <<
", CSA IM=" << netting->csaDetails()->applyInitialMargin()
180 <<
", CSA IM Type=" << initialMarginType
183 ALOG(
"ApplyInitialMargin deactivated at netting set level " << nettingSetId);
185 ALOG(
"ApplyInitialMargin deactivated in analytics, but active at netting set level " << nettingSetId);
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();
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_);
206 DLOG(
"Netting set " << nettingSetId <<
", IA base = VM base = 0");
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];
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);
242 for (Size j = 0; j <
cube_->dates().
size(); ++j) {
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) {
250 balance = collateral->at(k)->accountBalance(date);
254 netting->csaDetails()->csaCurrency());
259 eab[j + 1] += balance /
cube_->samples();
261 Real mporCashFlow = 0;
273 MporCashFlowMode::WePay) {
279 MporCashFlowMode::TheyPay) {
285 Real exposure =
data[j][k] - balance + mporCashFlow;
287 if (applyInitialMargin && collateral) {
293 QL_REQUIRE(dim >= 0,
"negative DIM for set " << nettingSetId <<
", date " << j <<
", sample " << k
298 if (initialMarginType != CSA::Type::PostOnly)
300 if (initialMarginType != CSA::Type::CallOnly)
304 epe[j + 1] += std::max(exposure - dim_epe, 0.0) /
cube_->samples();
306 ene[j + 1] += std::max(-exposure - dim_ene, 0.0) /
cube_->samples();
307 distribution[k] = exposure - dim_epe;
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);
322 if (netting->activeCsaFlag()) {
323 Real indexValue = 0.0;
324 DayCounter dc = ActualActual(ActualActual::ISDA);
325 if (csaIndexName !=
"") {
327 dc = csaIndex->dayCounter();
329 Real dcf = dc.yearFraction(prevDate, date);
330 Real collateralSpread = (balance >= 0.0 ? netting->csaDetails()->collatSpreadRcv() : netting->csaDetails()->collatSpreadPay());
332 Real colvaDelta = -balance * collateralSpread * dcf / numeraire /
cube_->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;
347 const auto& trade = tradeIt->second;
348 string nid = trade->envelope().nettingSetId();
349 if (nid != nettingSetId)
352 Real allocation = 0.0;
357 allocation = exposure / nettingSetSize[nid];
368 averagePositiveAllocation[i][j] += allocation /
cube_->samples();
370 averageNegativeAllocation[i][j] -= allocation /
cube_->samples();
379 ee_b[j + 1] =
epe[j + 1] / curve->discount(
cube_->dates()[j]);
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);
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);
402 while (t < cube_->dates().
size() && times[t] <= maturityTime)
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;
414 for (Size k = 0; k < t; k++) {
425 for (Size j = 0; j <
cube_->dates().
size(); ++j) {
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) {
440 QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>> collateral;
443 LOG(
"CSA missing or inactive for netting set " << nettingSetId);
448 QuantLib::ext::shared_ptr<CollateralBalance> balance =
nullptr;
451 LOG(
"got collateral balances for netting set " << nettingSetId);
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;
460 LOG(
"CSA FX rate for pair " << csaFxPair <<
" = " << csaFxRateToday);
463 Date today =
market_->asofDate();
464 string csaIndexName = netting->csaDetails()->index();
470 LOG(
"CSA compounding rate for index " << csaIndexName <<
" = " << setprecision(8) << csaRateToday <<
" as of " << today);
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));
477 "scenario data does not provide FX rates for " << csaFxPair);
479 if (csaIndexName !=
"") {
481 "scenario data does not provide index values for " << csaIndexName);
483 for (Size j = 0; j <
cube_->dates().
size(); ++j) {
484 for (Size k = 0; k <
cube_->samples(); ++k) {
489 csaScenFxRates[j][k] = 1.0;
490 if (csaIndexName !=
"") {
499 nettingSetValueToday,
510 LOG(
"Collateral account balance paths for netting set " << nettingSetId <<
" done");
516 vector<Real>
exp(
cube_->dates().size() + 1, 0.0);
518 for (Size i = 0; i <
cube_->dates().
size(); i++) {
535 "counterparty not found for netting set id " << nettingSetId);
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 >())
vector< Real > epe(const string &nid)
map< string, std::vector< Real > > pfe_
QuantLib::ext::shared_ptr< Market > market_
const Size allocatedEpeIndex_
map< string, vector< vector< Real > > > nettingSetDefaultValue()
QuantLib::ext::shared_ptr< NPVCube > tradeExposureCube_
map< string, vector< vector< Real > > > nettingSetDefaultValue_
map< string, vector< vector< Real > > > nettingSetMporPositiveFlow_
map< string, Real > collateralFloor_
const QuantLib::ext::shared_ptr< DynamicInitialMarginCalculator > dimCalculator_
const QuantLib::ext::shared_ptr< CollateralBalances > collateralBalances_
vector< Real > ene(const string &nid)
vector< Real > getMeanExposure(const string &tid, ExposureIndex index)
QuantLib::ext::shared_ptr< NPVCube > nettedCube_
const Real marginalAllocationLimit_
map< string, Real > eepe_b_
vector< Real > & eee_b(const string &nid)
map< string, Real > epe_b_
QuantLib::ext::shared_ptr< NPVCube > exposureCube_
map< string, string > counterpartyMap_
vector< Real > & pfe(const string &nid)
const Size allocatedEneIndex_
virtual void build()
Compute exposures along all paths and fill result structures.
const Size EXPOSURE_CUBE_DEPTH
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_
map< string, std::vector< Real > > eee_b_
const bool applyInitialMargin_
const string & counterparty(const string nettingSetId)
map< string, Real > colva_
const bool fullInitialCollateralisation_
QuantLib::ext::shared_ptr< CubeInterpretation > cubeInterpretation_
map< string, std::vector< Real > > eoniaFloorInc_
Real & epe_b(const string &nid)
map< string, vector< vector< Real > > > nettingSetCloseOutValue_
CollateralExposureHelper::CalculationType calcType_
map< string, std::vector< Real > > colvaInc_
Real & eepe_b(const string &nid)
MporCashFlowMode mporCashFlowMode_
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 > > ee_b_
map< string, std::vector< Real > > expectedCollateral_
map< string, vector< vector< Real > > > nettingSetMporNegativeFlow()
const QuantLib::ext::shared_ptr< NettingSetManager > nettingSetManager_
const bool marginalAllocation_
QuantLib::ext::shared_ptr< NPVCube > cube_
RandomVariable exp(RandomVariable x)
Size size(const ValueType &v)