Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Private Member Functions | Private Attributes | List of all members
CreditMigrationHelper Class Reference

#include <orea/aggregation/creditmigrationhelper.hpp>

+ Collaboration diagram for CreditMigrationHelper:

Public Types

enum class  CreditMode { Migration , Default }
 
enum class  LoanExposureMode { Notional , Value }
 
enum class  Evaluation { Analytic , ForwardSimulationA , ForwardSimulationB , TerminalSimulation }
 

Public Member Functions

 CreditMigrationHelper (const QuantLib::ext::shared_ptr< CreditSimulationParameters > parameters, const QuantLib::ext::shared_ptr< NPVCube > cube, const QuantLib::ext::shared_ptr< NPVCube > nettedCube, const QuantLib::ext::shared_ptr< AggregationScenarioData > aggData, const Size cubeIndexCashflows, const Size cubeIndexStateNpvs, const Real distributionLowerBound, const Real distributionUpperBound, const Size buckets, const Matrix &globalFactorCorrelation, const std::string &baseCurrency)
 
void build (const std::map< std::string, QuantLib::ext::shared_ptr< Trade > > &trades)
 builds the helper for a specific subset of trades stored in the cube More...
 
const std::vector< Real > & upperBucketBound () const
 
Array pnlDistribution (const Size date)
 

Private Member Functions

std::map< string, Matrix > rescaledTransitionMatrices (const Size date)
 
void init ()
 
void initEntityStateSimulation ()
 Allocate 3d storage for the simulated idiosyncratic factors by entity, date and sample. More...
 
std::vector< Matrix > initEntityStateSimulation (const Size date, const Size path)
 
void simulateEntityStates (const std::vector< Matrix > &cond, const Size path, const MersenneTwisterUniformRng &mt)
 
Size simulatedEntityState (const Size i, const Size path) const
 Look up the simulated entity credit state for the given entity, date and path. More...
 
Real generateMigrationPnl (const Size date, const Size path, const Size n) const
 
void generateConditionalMigrationPnl (const Size date, const Size path, const std::map< string, Matrix > &transMat, std::vector< Array > &condProbs, std::vector< Array > &pnl) const
 

Private Attributes

QuantLib::ext::shared_ptr< CreditSimulationParametersparameters_
 
QuantLib::ext::shared_ptr< NPVCubecube_
 
QuantLib::ext::shared_ptr< NPVCubenettedCube_
 
QuantLib::ext::shared_ptr< AggregationScenarioDataaggData_
 
Size cubeIndexCashflows_
 
Size cubeIndexStateNpvs_
 
Matrix globalFactorCorrelation_
 
std::string baseCurrency_
 
CreditMode creditMode_
 
LoanExposureMode loanExposureMode_
 
Evaluation evaluation_
 
std::vector< Real > cubeTimes_
 
QuantExt::Bucketing bucketing_
 
std::vector< std::set< std::string > > issuerTradeIds_
 
std::vector< std::set< std::string > > cptyNettingSetIds_
 
std::map< std::string, std::string > tradeCreditCurves_
 
std::map< std::string, Real > tradeNotionals_
 
std::map< std::string, std::string > tradeCurrencies_
 
std::map< std::string, Size > tradeCdsCptyIdx_
 
Size n_
 
std::vector< std::map< string, Matrix > > rescaledTransitionMatrices_
 
std::vector< Real > globalVar_
 
std::vector< std::vector< Size > > simulatedEntityState_
 
std::vector< std::vector< Matrix > > entityStateSimulationMatrices_
 
std::vector< std::vector< std::vector< Real > > > globalStates_
 

Detailed Description

Helper for credit migration risk calculation Dynamics of entity i's state X_i: \( dX_i = dY_i + dZ_i \) with

Definition at line 51 of file creditmigrationhelper.hpp.

Member Enumeration Documentation

◆ CreditMode

enum class CreditMode
strong

◆ LoanExposureMode

enum class LoanExposureMode
strong

◆ Evaluation

enum class Evaluation
strong

Constructor & Destructor Documentation

◆ CreditMigrationHelper()

CreditMigrationHelper ( const QuantLib::ext::shared_ptr< CreditSimulationParameters parameters,
const QuantLib::ext::shared_ptr< NPVCube cube,
const QuantLib::ext::shared_ptr< NPVCube nettedCube,
const QuantLib::ext::shared_ptr< AggregationScenarioData aggData,
const Size  cubeIndexCashflows,
const Size  cubeIndexStateNpvs,
const Real  distributionLowerBound,
const Real  distributionUpperBound,
const Size  buckets,
const Matrix &  globalFactorCorrelation,
const std::string &  baseCurrency 
)

Definition at line 41 of file creditmigrationhelper.cpp.

49 : parameters_(parameters), cube_(cube), nettedCube_(nettedCube), aggData_(aggData),
50 cubeIndexCashflows_(cubeIndexCashflows), cubeIndexStateNpvs_(cubeIndexStateNpvs),
51 globalFactorCorrelation_(globalFactorCorrelation), baseCurrency_(baseCurrency),
55 bucketing_(distributionLowerBound, distributionUpperBound, buckets) {
56
57 rescaledTransitionMatrices_.resize(cube_->numDates());
58 init();
61} // CreditMigrationHelper()
void initEntityStateSimulation()
Allocate 3d storage for the simulated idiosyncratic factors by entity, date and sample.
QuantLib::ext::shared_ptr< NPVCube > nettedCube_
QuantLib::ext::shared_ptr< CreditSimulationParameters > parameters_
QuantLib::ext::shared_ptr< AggregationScenarioData > aggData_
std::vector< std::map< string, Matrix > > rescaledTransitionMatrices_
QuantLib::ext::shared_ptr< NPVCube > cube_
CreditMigrationHelper::LoanExposureMode parseLoanExposureMode(const std::string &s)
CreditMigrationHelper::Evaluation parseEvaluation(const std::string &s)
CreditMigrationHelper::CreditMode parseCreditMode(const std::string &s)
+ Here is the call graph for this function:

Member Function Documentation

◆ build()

void build ( const std::map< std::string, QuantLib::ext::shared_ptr< Trade > > &  trades)

builds the helper for a specific subset of trades stored in the cube

Definition at line 584 of file creditmigrationhelper.cpp.

584 {
585 LOG("CreditMigrationHelper: Build trade ID map");
586 issuerTradeIds_.resize(parameters_->entities().size());
587 cptyNettingSetIds_.resize(parameters_->entities().size());
588 for (Size i = 0; i < parameters_->entities().size(); ++i) {
589 string entity = parameters_->entities()[i];
590 std::set<string> tmp, tmp2;
591 for (const auto& [_, t] : trades) {
592 if (t->issuer() == entity) {
593 tmp.insert(t->id());
594 }
595 if (t->envelope().counterparty() == entity &&
596 std::find(parameters_->nettingSetIds().begin(), parameters_->nettingSetIds().end(),
597 t->envelope().nettingSetId()) != parameters_->nettingSetIds().end()) {
598 tmp2.insert(t->envelope().nettingSetId());
599 }
600 QuantLib::ext::shared_ptr<Bond> bond = QuantLib::ext::dynamic_pointer_cast<Bond>(t);
601 if (bond) {
602 tradeCreditCurves_[t->id()] = bond->bondData().creditCurveId();
603 // FIXME: We actually need the notional schedule here to determine future notionals
604 tradeNotionals_[t->id()] = bond->notional();
605 tradeCurrencies_[t->id()] = bond->bondData().currency();
606 }
607 QuantLib::ext::shared_ptr<CreditDefaultSwap> cds = QuantLib::ext::dynamic_pointer_cast<CreditDefaultSwap>(t);
608 if (cds) {
609 string cpty = cds->envelope().counterparty();
610 QL_REQUIRE(cpty != t->issuer(), "CDS has same CPTY and issuer " << cpty);
611 Size idx = std::find(parameters_->entities().begin(), parameters_->entities().end(), cpty) -
612 parameters_->entities().begin();
613 if (idx < parameters_->entities().size())
614 tradeCdsCptyIdx_[t->id()] = idx;
615 else
616 WLOG("CreditMigrationHelepr: CDS trade "
617 << t->id() << " has cpty " << cpty
618 << " which is not in the list of simulated entities, "
619 "ignore joint default event of issuer and cpty for this CDS");
620 }
621 }
622 issuerTradeIds_[i] = tmp;
623 cptyNettingSetIds_[i] = tmp2;
624 }
625 LOG("CreditMigrationHelper: Built issuer and cpty trade ID sets for " << parameters_->entities().size()
626 << " entities.");
627 for (Size i = 0; i < parameters_->entities().size(); ++i) {
628 DLOG("Entity " << parameters_->entities()[i] << ": " << issuerTradeIds_[i].size()
629 << " trades with issuer risk, " << cptyNettingSetIds_[i].size()
630 << " nettings sets with derivative exposure risk.");
631 }
632}
std::vector< std::set< std::string > > issuerTradeIds_
std::vector< std::set< std::string > > cptyNettingSetIds_
std::map< std::string, std::string > tradeCreditCurves_
std::map< std::string, Size > tradeCdsCptyIdx_
std::map< std::string, Real > tradeNotionals_
std::map< std::string, std::string > tradeCurrencies_
#define LOG(text)
#define DLOG(text)
#define WLOG(text)
Size size(const ValueType &v)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ upperBucketBound()

const std::vector< Real > & upperBucketBound ( ) const

Definition at line 67 of file creditmigrationhelper.hpp.

67{ return bucketing_.upperBucketBound(); }
const std::vector< Real > & upperBucketBound() const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pnlDistribution()

Array pnlDistribution ( const Size  date)

Definition at line 447 of file creditmigrationhelper.cpp.

447 {
448
449 // FIXME if we ask this method for more than one time step, it might be more efficient to
450 // pass a vector of those time steps and compute the distirbutions in one sweep here
451 // in particular step 2b-1 (in forward simulation mode)
452
453 LOG("Compute PnL distribution for date " << date);
454 QL_REQUIRE(date < cube_->numDates(), "date index " << date << " out of range 0..." << cube_->numDates() - 1);
455 const std::vector<string>& entities = parameters_->entities();
456
457 // 1 get transition matrices for entities and rescale them to horizon
458
459 std::map<string, Matrix> transMat; // rescaled transition matrix per (matrix) name
460
461 // 1 get transition matrices for time step and compute variance of global factors for each entiity
462
463 if (parameters_->creditRisk()) {
464 transMat = rescaledTransitionMatrices(date);
465 }
466
467 // 2 compute conditional pnl distributions and average over paths
468
469 const std::set<std::string>& tradeIds = cube_->ids();
470 Array res(bucketing_.buckets(), 0.0);
471
472 Size numPaths = cube_->samples();
473 Real avgCash = 0.0;
474
476
477 MersenneTwisterUniformRng mt(parameters_->seed());
478
479 for (Size path = 0; path < numPaths; ++path) {
480
481 // 2a market pnl (t0 to horizon date, over whole cube)
482
483 Real cash = 0.0;
484
485 if (parameters_->marketRisk()) {
486 for (Size j = 0; j <= date + 1; ++j) {
487 for (auto const& tradeId : tradeIds) {
488 Size i = cube_->idsAndIndexes().at(tradeId);
489 // get cumulative survival probability on the path
490 Real sp = 1.0;
491 //Real rr = 0.0;
492 // FIXME 1
493 // Methodology question: Do we need/want to multiply with the stochastic discount factor
494 // here if we do an explicit credit default simulation at horizon?
495 // FIXME 2
496 // make CDS PnL neutral bei weighting flows with surv prob and generating protection flow
497 // with default prob
498 if (parameters_->zeroMarketPnl() && j > 0 &&
499 tradeCreditCurves_.find(tradeId) != tradeCreditCurves_.end()) {
500 string creditCurve = tradeCreditCurves_.at(tradeId);
501 sp = aggData_->get(j - 1, path, AggregationScenarioDataType::SurvivalWeight, creditCurve);
502 //rr = aggData_->get(j - 1, path, AggregationScenarioDataType::RecoveryRate, creditCurve);
503 }
504 if (j == 0) {
505 // at t0 we flip the sign of the npvs to get the initial cash balance
506 cash -= cube_->getT0(i, 0);
507 // collect intermediate cashflows
508 if (cubeIndexCashflows_ != Null<Size>())
509 cash += cube_->getT0(i, cubeIndexCashflows_);
510 } else if (j <= date) {
511 // collect intermediate cashflows
512 if (cubeIndexCashflows_ != Null<Size>())
513 cash += sp * cube_->get(i, j - 1, path, cubeIndexCashflows_);
514 } else {
515 // at the horizon date we realise the npv
516 cash += sp * cube_->get(i, j - 1, path, 0);
517 }
518 }
519 } // for data
520 } // if market risk
521
522 if (!parameters_->creditRisk()) {
523 // if we just add scalar market pnl realisations, we don't really need
524 // the bucketing algorithm to do that, we just update the result
525 // distribution directly
526 res[hwBucketing.index(cash)] += 1.0 / static_cast<Real>(numPaths);
527 continue;
528 }
529
530 // 2b credit migration pnl (at horizon date, over entities specified in credit simulation parameters)
531
532 std::vector<Array> condProbs, pnl;
533
535 // 2b-1 generate pnl on the path using simulated idiosyncratic factors
536 condProbs.resize(1, Array(parameters_->paths(), 1.0 / static_cast<Real>(parameters_->paths())));
537 // we could build the distribution more efficiently here, but later in 2c we add the market pnl
538 // maybe extend the hw bucketing so that we can feed precomputed distributions and just update
539 // these with additional data?
540 pnl.resize(1, Array(parameters_->paths(), 0.0));
541 auto cond = initEntityStateSimulation(date, path);
542 for (Size path2 = 0; path2 < parameters_->paths(); ++path2) {
543 simulateEntityStates(cond, path, mt);
544 pnl[0][path2] = generateMigrationPnl(date, path, n_);
545 }
546 } else {
547 // 2b-2 generate pnl distribution without simulation of idiosyncratic factors using the conditional
548 // independence of migration on the path / systemic factors
549
550 // n+1 states, since for CDS we have to subdivide the issuer default into
551 // i) default of issuer and non-default of CDS cpty
552 // ii) default of issuer, default of CDS cpty (but after the issuer default)
553 // iii) default of issuer, default of CDS cpty (before the issuer default)
554 // for non-CDS trades for all sub-states the pnl will be set to the same value
555 // for CDS trades i)+ii) will have the same pnl, but iii) will have a zero pnl
556 // in total, we only have to distinguish i)+ii) and iii), i.e. we need one
557 // additional state
558
559 condProbs.resize(entities.size(), Array(n_ + 1, 0.0));
560 pnl.resize(entities.size(), Array(n_ + 1, 0.0));
561 generateConditionalMigrationPnl(date, path, transMat, condProbs, pnl);
562 }
563
564 // 2c aggregate market pnl and credit migration pnl
565
566 if (parameters_->marketRisk()) {
567 condProbs.push_back(Array(1, 1.0));
568 pnl.push_back(Array(1, cash));
569 }
570
571 hwBucketing.computeMultiState(condProbs.begin(), condProbs.end(), pnl.begin());
572
573 // 2d add pnl contribution of path to result distribution
574 res += hwBucketing.probability() / static_cast<Real>(numPaths);
575 // average market risk pnl
576 avgCash += cash / static_cast<Real>(numPaths);
577
578 } // for path
579
580 DLOG("Expected Market Risk PnL at date " << date << ": " << avgCash);
581 return res;
582} // pnlDistribution
Size buckets() const
std::map< string, Matrix > rescaledTransitionMatrices(const Size date)
void generateConditionalMigrationPnl(const Size date, const Size path, const std::map< string, Matrix > &transMat, std::vector< Array > &condProbs, std::vector< Array > &pnl) const
Real generateMigrationPnl(const Size date, const Size path, const Size n) const
void simulateEntityStates(const std::vector< Matrix > &cond, const Size path, const MersenneTwisterUniformRng &mt)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rescaledTransitionMatrices()

std::map< string, Matrix > rescaledTransitionMatrices ( const Size  date)
private

Get the transition matrix from today to date by entity, sanitise the annual transition matrix input, rescale to the desired horizon/date using the generator, cache the result so that we do the sanitising/rescaling only once

Definition at line 87 of file creditmigrationhelper.cpp.

87 {
88
89 std::map<string, Matrix> transMat; // rescaled transition matrix per (matrix) name
90
91 // have we computed the result for the date index already?
92 if (!rescaledTransitionMatrices_[date].empty()) {
93 transMat = rescaledTransitionMatrices_[date];
94 } else {
95 Time t = cubeTimes_[date];
96
97 const std::vector<string>& entities = parameters_->entities();
98 const std::vector<string>& matrixNames = parameters_->transitionMatrices();
99
100 for (Size i = 0; i < entities.size(); ++i) {
101 if (transMat.count(matrixNames[i]) > 0) {
102 DLOG("Transition matrix for " << entities[i] << " (" << matrixNames[i] << ") cached, nothing to do.");
103 } else {
104 QL_REQUIRE(parameters_->transitionMatrix().count(parameters_->transitionMatrices()[i]) > 0,
105 "No transition matrix defined for " << parameters_->entities()[i] << " / "
106 << parameters_->transitionMatrices()[i]);
107 Matrix m = parameters_->transitionMatrix().at(parameters_->transitionMatrices()[i]);
108 DLOG("Transition matrix (1y) for " << parameters_->entities()[i] << " is "
109 << parameters_->transitionMatrices()[i] << ":");
110 DLOGGERSTREAM(m);
111 if (n_ == Null<Size>()) {
112 n_ = m.rows();
113 } else {
114 QL_REQUIRE(m.rows() == n_, "Found transition matrix with different dimension "
115 << m.rows() << "x" << m.columns() << " expected " << n_ << "x" << n_
116 << " for " << parameters_->entities()[i] << " / "
117 << parameters_->transitionMatrices()[i]);
118 }
119 QL_REQUIRE(date < cube_->numDates(),
120 "date index " << date << " outside range, cube has " << cube_->numDates() << " dates.");
122 DLOG("Sanitised transition matrix:");
123 DLOGGERSTREAM(m);
124 Matrix g = QuantExt::generator(m);
125 DLOG("Generator matrix:")
126 DLOGGERSTREAM(g);
128 Matrix mt = QuantExt::Expm(t * g);
129 DLOG("Scaled transition matrix (t=" << t << "):");
130 DLOGGERSTREAM(mt);
132 Matrix mcheck = m;
133 for (Size c = 1; c < static_cast<Size>(std::round(t)); ++c) {
134 mcheck = mcheck * m;
135 }
136 DLOG("Elementary transition matrix (t=" << std::round(t) << ", just for plausibility):");
137 DLOGGERSTREAM(mcheck);
138 transMat[matrixNames[i]] = mt;
139 }
140 }
141 rescaledTransitionMatrices_[date] = transMat;
142 }
143
144 return transMat;
145} // rescaledTransitionMatrices
#define DLOGGERSTREAM(text)
QuantLib::Matrix Expm(const QuantLib::Matrix &m)
void checkTransitionMatrix(const Matrix &t)
void checkGeneratorMatrix(const Matrix &g)
Matrix generator(const Matrix &t, const Real horizon)
void sanitiseTransitionMatrix(Matrix &m)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ init()

void init ( )
private

Initialise

  • the variance of the global part Y_i of entity state X_i, for all entities
  • the global part Y_i of entity i's state X_i by date index, entity index and sample number using the simulated global state paths stored in the aggregation scenario data object

Definition at line 147 of file creditmigrationhelper.cpp.

147 {
148
149 LOG("CreditMigrationHelper Init");
150
151 n_ = Null<Size>(); // number of states
152
153 Date asof = cube_->asof();
154 for (Size d = 0; d < cube_->numDates(); ++d) {
155 Date tDate = cube_->dates()[d];
156 cubeTimes_.push_back(
157 ActualActual(ActualActual::ISDA).yearFraction(asof, tDate)); // FIME make dc consistent with sim
158 }
159
160 const std::vector<Array>& loadings = parameters_->factorLoadings();
161 Size f = globalFactorCorrelation_.rows();
162 globalVar_.resize(parameters_->entities().size(), 0.0);
163 for (Size i = 0; i < parameters_->entities().size(); ++i) {
164 // variance of global factors
165 QL_REQUIRE(parameters_->factorLoadings()[i].size() == f, "wrong size for loadings for entity "
166 << parameters_->entities()[i] << " ("
167 << loadings.size() << "), expected " << f);
168 // don't optimise, code is simpler, matrix is small
169 for (Size j = 0; j < loadings[i].size(); ++j) {
170 for (Size k = 0; k < loadings[i].size(); ++k) {
171 globalVar_[i] += loadings[i][j] * loadings[i][k] * globalFactorCorrelation_[j][k];
172 }
173 }
174 }
175
176 globalStates_.resize(cube_->numDates(), std::vector<std::vector<Real>>(parameters_->entities().size(),
177 std::vector<Real>(cube_->samples())));
178 Array globalFactors(f);
179 std::vector<string> numStr(f);
180 for (Size i = 0; i < f; ++i) {
181 std::ostringstream num;
182 num << i;
183 numStr[i] = num.str();
184 }
185 for (Size d = 0; d < cube_->numDates(); ++d) {
186 for (Size i = 0; i < parameters_->entities().size(); ++i) {
187 for (Size j = 0; j < cube_->samples(); ++j) {
188 for (Size ii = 0; ii < f; ++ii) {
189 globalFactors[ii] = aggData_->get(d, j, AggregationScenarioDataType::CreditState, numStr[ii]);
190 }
191 globalStates_[d][i][j] = DotProduct(loadings[i], globalFactors / std::sqrt(cubeTimes_[d]));
192 }
193 }
194 }
195
196 LOG("CreditMigration Init done.");
197
198} // init
std::vector< std::vector< std::vector< Real > > > globalStates_
Date asof(14, Jun, 2018)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ initEntityStateSimulation() [1/2]

void initEntityStateSimulation ( )
private

Allocate 3d storage for the simulated idiosyncratic factors by entity, date and sample.

Definition at line 200 of file creditmigrationhelper.cpp.

200 {
201
202 LOG("Init entity state simulation");
203
205 std::vector<std::vector<Size>>(parameters_->entities().size(), std::vector<Size>(cube_->samples()));
206
207 LOG("Init entity state simulation done.");
208
209} // initEntityStatesSimulation
std::vector< std::vector< Size > > simulatedEntityState_
+ Here is the caller graph for this function:

◆ initEntityStateSimulation() [2/2]

std::vector< Matrix > initEntityStateSimulation ( const Size  date,
const Size  path 
)
private

Initialise the entity state simulationn for a given date for Evaluation = TerminalSimulation: Return transition matrix for each entity for the given date, conditional on the global terminal state on the given path

Definition at line 211 of file creditmigrationhelper.cpp.

211 {
212 std::vector<Matrix> res = std::vector<Matrix>(parameters_->entities().size(), Matrix(n_, n_, 0.0));
213
214 const std::vector<string>& matrixNames = parameters_->transitionMatrices();
215
216 // build terminal matrices conditional on global states
217 Size numWarnings = 0;
218 auto transMat = rescaledTransitionMatrices(date);
219 for (Size i = 0; i < parameters_->entities().size(); ++i) {
220 const Matrix& m = transMat.at(matrixNames[i]);
221 for (Size ii = 0; ii < m.rows(); ++ii) {
222 Real p = 0.0, condProb0 = 0.0;
223 for (Size jj = 0; jj < m.columns(); ++jj) {
224 p += m[ii][jj];
225 Real condProb = conditionalProb(p, globalStates_[date][i][path], globalVar_[i]);
226 res[i][ii][jj] = condProb - condProb0;
227 condProb0 = condProb;
228 }
229 }
230 try {
231 checkTransitionMatrix(res[i]);
232 } catch (const std::exception& e) {
233 if (++numWarnings <= 10) {
234 WLOG("Invalid conditional transition matrix (path=" << path << ", date=" << date << ", entity =" << i
235 << ": " << e.what());
236 } else if (numWarnings == 11) {
237 WLOG("Suppress further warnings on invalid conditional transition matrices");
238 }
240 }
241 }
242
243 // ... and finally build partial sums over columns for the simulation
244 for (Size i = 0; i < parameters_->entities().size(); ++i) {
245 Matrix& m = res[i];
246 for (Size ii = 0; ii < m.rows(); ++ii) {
247 for (Size jj = 1; jj < m.columns(); ++jj) {
248 m[ii][jj] += m[ii][jj - 1];
249 }
250 }
251 }
252
253 return res;
254}
+ Here is the call graph for this function:

◆ simulateEntityStates()

void simulateEntityStates ( const std::vector< Matrix > &  cond,
const Size  path,
const MersenneTwisterUniformRng &  mt 
)
private

Generate one entity state sample path for all entities given the global state path and given the conditional transition matrices for all entities at the terminal date.

Definition at line 256 of file creditmigrationhelper.cpp.

257 {
258
259 QL_REQUIRE(evaluation_ != Evaluation::Analytic,
260 "CreditMigrationHelper::simulateEntityStates() unexpected call, not in simulation mode");
261
262 for (Size i = 0; i < parameters_->entities().size(); ++i) {
263 Size initialState = parameters_->initialStates()[i];
264 Real tmp = mt.next().value;
265 Size entityState =
266 std::lower_bound(cond[i].row_begin(initialState), cond[i].row_end(initialState), tmp) -
267 cond[i].row_begin(initialState);
268 entityState = std::min(entityState, cond[i].columns() - 1); // play safe
269 simulatedEntityState_[i][path] = entityState;
270 initialState = entityState;
271 }
272
273} // simulateEntityStates
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ simulatedEntityState()

Size simulatedEntityState ( const Size  i,
const Size  path 
) const
private

Look up the simulated entity credit state for the given entity, date and path.

Definition at line 275 of file creditmigrationhelper.cpp.

275 {
276 QL_REQUIRE(evaluation_ != Evaluation::Analytic,
277 "CreditMigrationHelper::simulatedEntityState() unexpected call, not in simulation mode");
278 return simulatedEntityState_[i][path];
279} // simulatedEntityState
+ Here is the caller graph for this function:

◆ generateMigrationPnl()

Real generateMigrationPnl ( const Size  date,
const Size  path,
const Size  n 
) const
private

Return a single PnL impact due to credit migration or default of Bond/CDS issuers and default of netting set counterparties on the given global path

Definition at line 281 of file creditmigrationhelper.cpp.

281 {
282
283 QL_REQUIRE(!parameters_->doubleDefault(),
284 "CreditMigrationHelper::generateMigrationPnl() does not support double default");
285
286 const std::vector<string>& entities = parameters_->entities();
287 Real pnl = 0.0;
288
289 for (Size i = 0; i < entities.size(); ++i) {
290 // compute credit state of entitiy
291 // issuer migration risk
292 Size simEntityState = simulatedEntityState(i, path);
293 for (auto const& tradeId : issuerTradeIds_[i]) {
294 try {
295 Size tid = cube_->idsAndIndexes().at(tradeId);
296 Real baseValue = cube_->get(tid, date, path, 0);
297 Real stateValue = cube_->get(tid, date, path, cubeIndexStateNpvs_ + simEntityState);
299 if (tradeNotionals_.find(tradeId) != tradeNotionals_.end()) {
300 // this is a bond
301 string tradeCcy = tradeCurrencies_.at(tradeId);
302 string ccypair = tradeCcy + baseCurrency_;
303 Real fx = 1.0;
304 if (tradeCcy != baseCurrency_) {
305 QL_REQUIRE(aggData_->has(AggregationScenarioDataType::FXSpot, ccypair),
306 "FX spot data not found in aggregation data for currency pair " << ccypair);
307 fx = aggData_->get(date, path, AggregationScenarioDataType::FXSpot, ccypair);
308 }
309 // FIXME: We actually need the correct current notional as of the future horizon date,
310 // but we have the current notional as of today
311 baseValue = tradeNotionals_.at(tradeId) * fx;
312 // FIXME: get the bond's recovery rate
313 Real rr = 0.0;
314 stateValue = simEntityState == n - 1 ? rr * baseValue : baseValue;
315 }
316 if (tradeCdsCptyIdx_.find(tradeId) != tradeCdsCptyIdx_.end()) {
317 // this is a cds
318 baseValue = 0.0;
319 if (simEntityState < n - 1)
320 stateValue = 0.0;
321 else
322 stateValue *= aggData_->get(date, path, AggregationScenarioDataType::Numeraire);
323 }
324 }
325 if (creditMode_ == CreditMode::Default && simEntityState < n - 1) {
326 stateValue = baseValue;
327 }
328 pnl += stateValue - baseValue;
329 } catch (const std::exception& e) {
330 ALOG("can not get state npv for trade " << tradeId << " (reason:" << e.what() << "), state "
331 << simEntityState << ", assume zero credit migration pnl");
332 }
333 }
334 // default risk for derivative exposure
335 // TODO, assuming a zero recovery here...
336 for (auto const& nettingSetId : cptyNettingSetIds_[i]) {
337 Size nid = nettedCube_->idsAndIndexes().at(nettingSetId);
338 QL_REQUIRE(nettedCube_, "empty netted cube");
339 if (simEntityState == n - 1)
340 pnl -= std::max(nettedCube_->get(nid, date, path), 0.0);
341 }
342 }
343 return pnl;
344} // generateMigrationPnl
Size simulatedEntityState(const Size i, const Size path) const
Look up the simulated entity credit state for the given entity, date and path.
#define ALOG(text)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ generateConditionalMigrationPnl()

void generateConditionalMigrationPnl ( const Size  date,
const Size  path,
const std::map< string, Matrix > &  transMat,
std::vector< Array > &  condProbs,
std::vector< Array > &  pnl 
) const
private

Return a vector of PnL impacts and associated conditional probabilities for the specified global path, due to credit migration or default of Bond/CDS issuers and default of netting set counterparties

Definition at line 346 of file creditmigrationhelper.cpp.

349 {
350
351 Real t = cubeTimes_[date];
352
353 const std::vector<string>& entities = parameters_->entities();
354 const std::vector<string>& matrixNames = parameters_->transitionMatrices();
355
356 for (Size i = 0; i < entities.size(); ++i) {
357 // compute conditional migration prob
358 Size initialState = parameters_->initialStates()[i];
359 Real p = 0.0, condProb0 = 0.0;
360 const Matrix& m = transMat.at(matrixNames[i]);
361 for (Size j = 0; j < n_; ++j) {
362 p += m[initialState][j];
363 Real condProb = conditionalProb(p, globalStates_[date][i][path], globalVar_[i]);
364 condProbs[i][j] = condProb - condProb0;
365 condProb0 = condProb;
366 }
367 // issuer migration risk
368 Size cdsCptyIdx = Null<Size>();
369 for (auto const& tradeId : issuerTradeIds_[i]) {
370 for (Size j = 0; j < n_; ++j) {
371 try {
372 Size tid = cube_->idsAndIndexes().at(tradeId);
373 Real baseValue = cube_->get(tid, date, path, 0);
374 Real stateValue = cube_->get(tid, date, path, cubeIndexStateNpvs_ + j);
376 if (tradeNotionals_.find(tradeId) != tradeNotionals_.end()) {
377 // this is a bond
378 string tradeCcy = tradeCurrencies_.at(tradeId);
379 string ccypair = tradeCcy + baseCurrency_;
380 Real fx = 1.0;
381 if (tradeCcy != baseCurrency_) {
382 QL_REQUIRE(aggData_->has(AggregationScenarioDataType::FXSpot, ccypair),
383 "FX spot data not found in aggregation data for currency pair " << ccypair);
384 fx = aggData_->get(date, path, AggregationScenarioDataType::FXSpot, ccypair);
385 }
386 // FIXME: We actually need the correct current notional as of the future horizon date,
387 // but we have the current notional as of today
388 baseValue = tradeNotionals_.at(tradeId) * fx;
389 // FIXME: get the bond's recovery rate
390 Real rr = 0.0;
391 stateValue = j == n_ - 1 ? rr * baseValue : baseValue;
392 }
393 if (tradeCdsCptyIdx_.find(tradeId) != tradeCdsCptyIdx_.end()) {
394 // this is a cds
395 baseValue = 0.0;
396 if (j < n_ - 1)
397 stateValue = 0.0;
398 else
399 stateValue *= aggData_->get(date, path, AggregationScenarioDataType::Numeraire);
400 }
401 }
402 if (creditMode_ == CreditMode::Default && j < n_ - 1) {
403 stateValue = baseValue;
404 }
405 pnl[i][j] += stateValue - baseValue;
406 // pnl for additional double default state
407 if (j == n_ - 1)
408 pnl[i][n_] += stateValue - baseValue;
409 // for a CDS we have to subdivide the default migration event into two events (see above)
410 if (parameters_->doubleDefault() && j == n_ - 1 &&
411 tradeCdsCptyIdx_.find(tradeId) != tradeCdsCptyIdx_.end()) {
412 // FIXME currently we can not handle two CDS cptys for same underlying issuer
413 QL_REQUIRE(cdsCptyIdx == Null<Size>() || cdsCptyIdx == tradeCdsCptyIdx_.at(tradeId),
414 "CreditMigrationHelper: Two different CDS cptys found for same issuer "
415 << entities[i]);
416 // only adjust probability once
417 if (cdsCptyIdx == Null<Size>()) {
418 Real cptyDefaultPd =
419 transMat.at(matrixNames[tradeCdsCptyIdx_.at(tradeId)])[initialState][n_ - 1];
420 Real pd = prob_tauA_lt_tauB_lt_T(cptyDefaultPd, condProbs[i][n_ - 1], t);
421 QL_REQUIRE(pd <= condProbs[i][n_ - 1],
422 "CreditMigrationHelper: unexpected probability for double default event "
423 << pd << " > " << condProbs[i][n_ - 1]);
424 condProbs[i][n_ - 1] -= pd;
425 condProbs[i][n_] = pd;
426 cdsCptyIdx = tradeCdsCptyIdx_.at(tradeId);
427 // pnl for new state is zero
428 pnl[i][n_] -= stateValue - baseValue;
429 }
430 }
431 } catch (const std::exception& e) {
432 ALOG("can not get state npv for trade " << tradeId << " (reason:" << e.what() << "), state " << j
433 << ", assume zero credit migration pnl");
434 }
435 }
436 }
437 // default risk for derivative exposure
438 // TODO, assuming a zero recovery here...
439 for (auto const& nettingSetId : cptyNettingSetIds_[i]) {
440 auto nid = nettedCube_->idsAndIndexes().at(nettingSetId);
441 QL_REQUIRE(nettedCube_, "empty netted cube");
442 pnl[i][n_ - 1] -= std::max(nettedCube_->get(nid, date, path), 0.0);
443 }
444 }
445} // generateConditionalMigrationPnl
+ Here is the caller graph for this function:

Member Data Documentation

◆ parameters_

QuantLib::ext::shared_ptr<CreditSimulationParameters> parameters_
private

Definition at line 109 of file creditmigrationhelper.hpp.

◆ cube_

QuantLib::ext::shared_ptr<NPVCube> cube_
private

Definition at line 110 of file creditmigrationhelper.hpp.

◆ nettedCube_

QuantLib::ext::shared_ptr<NPVCube> nettedCube_
private

Definition at line 110 of file creditmigrationhelper.hpp.

◆ aggData_

QuantLib::ext::shared_ptr<AggregationScenarioData> aggData_
private

Definition at line 111 of file creditmigrationhelper.hpp.

◆ cubeIndexCashflows_

Size cubeIndexCashflows_
private

Definition at line 112 of file creditmigrationhelper.hpp.

◆ cubeIndexStateNpvs_

Size cubeIndexStateNpvs_
private

Definition at line 112 of file creditmigrationhelper.hpp.

◆ globalFactorCorrelation_

Matrix globalFactorCorrelation_
private

Definition at line 113 of file creditmigrationhelper.hpp.

◆ baseCurrency_

std::string baseCurrency_
private

Definition at line 114 of file creditmigrationhelper.hpp.

◆ creditMode_

CreditMode creditMode_
private

Definition at line 116 of file creditmigrationhelper.hpp.

◆ loanExposureMode_

LoanExposureMode loanExposureMode_
private

Definition at line 117 of file creditmigrationhelper.hpp.

◆ evaluation_

Evaluation evaluation_
private

Definition at line 118 of file creditmigrationhelper.hpp.

◆ cubeTimes_

std::vector<Real> cubeTimes_
private

Definition at line 119 of file creditmigrationhelper.hpp.

◆ bucketing_

QuantExt::Bucketing bucketing_
private

Definition at line 121 of file creditmigrationhelper.hpp.

◆ issuerTradeIds_

std::vector<std::set<std::string> > issuerTradeIds_
private

Definition at line 123 of file creditmigrationhelper.hpp.

◆ cptyNettingSetIds_

std::vector<std::set<std::string> > cptyNettingSetIds_
private

Definition at line 124 of file creditmigrationhelper.hpp.

◆ tradeCreditCurves_

std::map<std::string, std::string> tradeCreditCurves_
private

Definition at line 126 of file creditmigrationhelper.hpp.

◆ tradeNotionals_

std::map<std::string, Real> tradeNotionals_
private

Definition at line 127 of file creditmigrationhelper.hpp.

◆ tradeCurrencies_

std::map<std::string, std::string> tradeCurrencies_
private

Definition at line 128 of file creditmigrationhelper.hpp.

◆ tradeCdsCptyIdx_

std::map<std::string, Size> tradeCdsCptyIdx_
private

Definition at line 129 of file creditmigrationhelper.hpp.

◆ n_

Size n_
private

Definition at line 132 of file creditmigrationhelper.hpp.

◆ rescaledTransitionMatrices_

std::vector<std::map<string, Matrix> > rescaledTransitionMatrices_
private

Definition at line 133 of file creditmigrationhelper.hpp.

◆ globalVar_

std::vector<Real> globalVar_
private

Definition at line 135 of file creditmigrationhelper.hpp.

◆ simulatedEntityState_

std::vector<std::vector<Size> > simulatedEntityState_
private

Definition at line 137 of file creditmigrationhelper.hpp.

◆ entityStateSimulationMatrices_

std::vector<std::vector<Matrix> > entityStateSimulationMatrices_
private

Definition at line 139 of file creditmigrationhelper.hpp.

◆ globalStates_

std::vector<std::vector<std::vector<Real> > > globalStates_
private

Definition at line 141 of file creditmigrationhelper.hpp.