Compute exposures along all paths and fill result structures.
84 {
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();
105 else {
106 QL_REQUIRE(
counterpartyMap_[nettingSetId] == cp,
"counterparty name is not unique within the netting set");
107 }
108 Real npv;
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;
133 string nettingSetId = n.first;
134 vector<vector<Real>>
data = n.second;
135 QuantLib::ext::shared_ptr<NettingSetDefinition> netting =
nettingSetManager_->get(nettingSetId);
136
137
138 QuantLib::ext::shared_ptr<CollateralBalance> balance = nullptr;
141 DLOG(
"got collateral balances for netting set " << nettingSetId);
142 }
143
144
147
150
151 LOG(
"Aggregate exposure for netting set " << nettingSetId);
152
153
154 QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>> collateral =
156 nettingSetValueToday[nettingSetId],
158 nettingSetMaturity[nettingSetId]);
159
160
161 colva_[nettingSetId] = 0.0;
163 string csaIndexName;
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);
173 }
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);
186 }
187
188
189
190
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;
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
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];
224
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
234
235 eab[0] = npv;
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);
252
254 netting->csaDetails()->csaCurrency());
255 balance *= fxRate;
256 }
257 }
258
259 eab[j + 1] += balance /
cube_->samples();
260
261 Real mporCashFlow = 0;
262
263
266
267
268 mporCashFlow = 0;
270
273 MporCashFlowMode::WePay) {
274
275
276
279 MporCashFlowMode::TheyPay) {
280
281
283 }
284 }
285 Real exposure =
data[j][k] - balance + mporCashFlow;
286 Real dim = 0.0;
287 if (applyInitialMargin && collateral) {
288
289
290
291 Size dimIndex = j;
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
304 epe[j + 1] += std::max(exposure - dim_epe, 0.0) /
cube_->samples();
305
306 ene[j + 1] += std::max(-exposure - dim_ene, 0.0) /
cube_->samples();
307 distribution[k] = exposure - dim_epe;
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
320 }
321
322 if (netting->activeCsaFlag()) {
323 Real indexValue = 0.0;
324 DayCounter dc = ActualActual(ActualActual::ISDA);
325 if (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());
332 Real colvaDelta = -balance * collateralSpread * dcf / numeraire /
cube_->samples();
333
334
335
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;
341 }
342
344 Size i = 0;
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)
355
357 allocation = exposure / nettingSetSize[nid];
358 else
360
362 if (exposure > 0.0)
364 else
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 }
378 }
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);
384 }
391
392 nettingSetCount++;
393
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++) {
417 }
418 }
421 }
422
425 for (Size j = 0; j <
cube_->dates().
size(); ++j) {
428 }
429 }
430 }
431}
vector< Real > epe(const string &nid)
map< string, std::vector< Real > > pfe_
map< string, Real > collateralFloor_
vector< Real > ene(const string &nid)
map< string, Real > eepe_b_
vector< Real > & eee_b(const string &nid)
map< string, Real > epe_b_
map< string, string > counterpartyMap_
vector< Real > & pfe(const string &nid)
map< string, std::vector< Real > > eee_b_
map< string, Real > colva_
map< string, std::vector< Real > > eoniaFloorInc_
Real & epe_b(const string &nid)
map< string, std::vector< Real > > colvaInc_
Real & eepe_b(const string &nid)
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_
Size size(const ValueType &v)