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

Collateral Exposure Helper. More...

#include <orea/aggregation/collatexposurehelper.hpp>

+ Collaboration diagram for CollateralExposureHelper:

Public Types

enum  CalculationType { Symmetric , AsymmetricCVA , AsymmetricDVA , NoLag }
 

Static Public Member Functions

static Real marginRequirementCalc (const QuantLib::ext::shared_ptr< CollateralAccount > &collat, const Real &uncollatValue, const Date &simulationDate)
 
template<class T >
static Real estimateUncollatValue (const Date &simulationDate, const Real &npv_t0, const Date &date_t0, const vector< vector< T > > &scenPvProfiles, const unsigned &scenIndex, const vector< Date > &dateGrid)
 
static void updateMarginCall (const QuantLib::ext::shared_ptr< CollateralAccount > &collat, const Real &uncollatValue, const Date &simulationDate, const Real &accrualFactor, const CalculationType &calcType=Symmetric, const bool &eligMarginReqDateUs=true, const bool &eligMarginReqDateCtp=true)
 
static Real creditSupportAmount (const QuantLib::ext::shared_ptr< ore::data::NettingSetDefinition > &nettingSet, const Real &uncollatValueCsaCur)
 
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 >())
 

Detailed Description

Collateral Exposure Helper.

This class contains helper functions to aid in the calculation of collateralised exposures.

It can be used to calculate margin requirements in the presence of e.g. thresholds and minimum transfer amounts, update collateral account details with e.g. new margin call info, and return collateralised exposures to the user/invoker.

For further information refer to the detailed ORE documentation.

Definition at line 51 of file collatexposurehelper.hpp.

Member Enumeration Documentation

◆ CalculationType

Enumeration 'CalculationType' specifies how the collateralised exposures should be calculated (please refer to Sungard white-paper titled "Closing In On the CloseOut"):

  • 'Symmetric' => margin calls only settled after margin period of risk
  • 'AsymmetricCVA' => margin requested from ctp only settles after margin period of risk – (our margin postings settle instantaneously)
  • 'AsymmetricDVA' => margin postings to ctp only settle after margin period of risk – (margin calls to receive collateral from counterparty settle instantaneously)
  • 'NoLag' => margin calls/postings settled without margin period of risk delay
Enumerator
Symmetric 
AsymmetricCVA 
AsymmetricDVA 
NoLag 

Definition at line 64 of file collatexposurehelper.hpp.

Member Function Documentation

◆ marginRequirementCalc()

Real marginRequirementCalc ( const QuantLib::ext::shared_ptr< CollateralAccount > &  collat,
const Real &  uncollatValue,
const Date &  simulationDate 
)
static

Calculates CSA margin requirement, taking the following into account

  • uncollateralised value
  • collateral value
  • threshold
  • minimum transfer amount
  • independent amount

Definition at line 60 of file collatexposurehelper.cpp.

61 {
62 // first step, make sure collateral balance is up to date.
63 // collat->updateAccountBalance(simulationDate);
64
65 Real collatBalance = collat->accountBalance();
66 Real csa = creditSupportAmount(collat->csaDef(), uncollatValue);
67
68 Real openMargins = collat->outstandingMarginAmount(simulationDate);
69 Real collatShortfall = csa - collatBalance - openMargins;
70
71 Real mta;
72 if (collatShortfall >= 0.0)
73 mta = collat->csaDef()->csaDetails()->mtaRcv();
74 else
75 mta = collat->csaDef()->csaDetails()->mtaPay();
76
77 Real deliveryAmount = fabs(collatShortfall) >= mta ? (collatShortfall) : 0.0;
78
79 return deliveryAmount;
80}
static Real creditSupportAmount(const QuantLib::ext::shared_ptr< ore::data::NettingSetDefinition > &nettingSet, const Real &uncollatValueCsaCur)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ estimateUncollatValue()

Real estimateUncollatValue ( const Date &  simulationDate,
const Real &  npv_t0,
const Date &  date_t0,
const vector< vector< T > > &  scenPvProfiles,
const unsigned &  scenIndex,
const vector< Date > &  dateGrid 
)
static

Performs linear interpolation between dates to estimate the value as of simulationDate. Flat extrapolation at far end.

Definition at line 101 of file collatexposurehelper.cpp.

103 {
104
105 QL_REQUIRE(simulationDate >= date_t0, "CollatExposureHelper error: simulation date < start date");
106 QL_REQUIRE(dateGrid[0] >= date_t0, "CollatExposureHelper error: cube dateGrid starts before t0");
107
108 if (simulationDate >= dateGrid.back())
109 return scenPvProfiles.back()[scenIndex]; // flat extrapolation
110 if (simulationDate == date_t0)
111 return npv_t0;
112 for (unsigned i = 0; i < dateGrid.size(); i++) {
113 if (dateGrid[i] == simulationDate)
114 // return if collat sim-date is the same as an exposure grid date
115 return scenPvProfiles[i][scenIndex];
116#ifdef FLAT_INTERPOLATION
117 else if (simulationDate < dateGrid.front())
118 return scenPvProfiles.front()[scenIndex];
119 else if (i < dateGrid.size() - 1 && simulationDate > dateGrid[i] && simulationDate < dateGrid[i + 1])
120 return scenPvProfiles[i + 1][scenIndex];
121#endif
122 }
123
124 // if none of above criteria are met, perform interpolation
125 Date t1, t2;
126 Real npv1, npv2;
127 if (simulationDate <= dateGrid[0]) {
128 t1 = date_t0;
129 t2 = dateGrid[0];
130 npv1 = npv_t0;
131 npv2 = scenPvProfiles[0][scenIndex];
132 } else {
133 vector<Date>::const_iterator it = lower_bound(dateGrid.begin(), dateGrid.end(), simulationDate);
134 QL_REQUIRE(it != dateGrid.end(), "CollatExposureHelper error; "
135 << "date interpolation points not found (it.end())");
136 QL_REQUIRE(it != dateGrid.begin(), "CollatExposureHelper error; "
137 << "date interpolation points not found (it.begin())");
138 Size pos1 = (it - 1) - dateGrid.begin();
139 Size pos2 = it - dateGrid.begin();
140 t1 = dateGrid[pos1];
141 t2 = dateGrid[pos2];
142 npv1 = scenPvProfiles[pos1][scenIndex];
143 npv2 = scenPvProfiles[pos2][scenIndex];
144 }
145
146 Real newPv = npv1 + ((npv2 - npv1) * (double(simulationDate - t1) / double(t2 - t1)));
147 QL_REQUIRE((npv1 <= newPv && newPv <= npv2) || (npv1 >= newPv && newPv >= npv2),
148 "CollatExposureHelper error; "
149 << "interpolated Pv value " << newPv << " out of range (" << npv1 << " " << npv2 << ") "
150 << "for simulation date " << simulationDate << " between " << t1 << " and " << t2);
151
152 return newPv;
153}
+ Here is the caller graph for this function:

◆ updateMarginCall()

void updateMarginCall ( const QuantLib::ext::shared_ptr< CollateralAccount > &  collat,
const Real &  uncollatValue,
const Date &  simulationDate,
const Real &  accrualFactor,
const CalculationType calcType = Symmetric,
const bool eligMarginReqDateUs = true,
const bool eligMarginReqDateCtp = true 
)
static

Checks if margin call is in need of update, and updates if necessary

Definition at line 155 of file collatexposurehelper.cpp.

158 {
159 collat->updateAccountBalance(simulationDate, annualisedZeroRate);
160
161 Real margin = marginRequirementCalc(collat, uncollatValue, simulationDate);
162
163 if (margin != 0.0) {
164 // settle margin call on appropriate date
165 // (dependent upon MPR and collateralised calculation methodology)
166 Date marginPayDate;
167 // RL 2020-07-17
168 // 1) If the calculation type is set to NoLag:
169 // Collateral balances are NOT delayed by the MPoR, but we use the close-out NPV in exposure calculations,
170 // see similar comment and the equivalent change in the post processor.
171 // 2) Otherwise:
172 // Collateral balances are delayed by the MPoR (if possible, i.e. the valuation grid has MPoR spacing),
173 // and we use the default date NPV.
174 // This is the treatment in the ORE releases up to June 2020).
175 Period lag = (calcType == NoLag ? 0*Days : collat->csaDef()->csaDetails()->marginPeriodOfRisk());
176 if (margin > 0.0 && eligMarginReqDateUs) {
177 marginPayDate =
178 (calcType == AsymmetricDVA ? simulationDate : simulationDate + lag);
179 collat->updateMarginCall(margin, marginPayDate, simulationDate);
180 } else if (margin < 0.0 && eligMarginReqDateCtp) {
181 marginPayDate =
182 (calcType == AsymmetricCVA ? simulationDate : simulationDate + lag);
183 collat->updateMarginCall(margin, marginPayDate, simulationDate);
184 } else {
185 }
186 }
187}
static Real marginRequirementCalc(const QuantLib::ext::shared_ptr< CollateralAccount > &collat, const Real &uncollatValue, const Date &simulationDate)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ creditSupportAmount()

Real creditSupportAmount ( const QuantLib::ext::shared_ptr< ore::data::NettingSetDefinition > &  nettingSet,
const Real &  uncollatValueCsaCur 
)
static

Computes the Credit Support Amount for the portfolio, given an unsecured exposure as input All calculations done in CSA currency

Definition at line 82 of file collatexposurehelper.cpp.

84 {
85
86 Real ia = nettingSet->csaDetails()->independentAmountHeld();
87 Real threshold, csa;
88 if (uncollatValueCsaCur + ia >= 0) {
89 threshold = nettingSet->csaDetails()->thresholdRcv();
90 csa = max(uncollatValueCsaCur + ia - threshold, 0.0);
91 }
92 else {
93 threshold = nettingSet->csaDetails()->thresholdPay();
94 // N.B. the min and change of sign on threshold.
95 csa = min(uncollatValueCsaCur + ia + threshold, 0.0);
96 }
97 return csa;
98}
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable min(RandomVariable x, const RandomVariable &y)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ collateralBalancePaths()

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>() 
)
static

Takes a netting set (and scenario exposures) as input and returns collateral balance paths per scenario

Definition at line 189 of file collatexposurehelper.cpp.

194 {
195
196 try {
197 // step 1; build a collateral account object, assuming t0 VM balance from the balance object (zero balance if missing),
198 // and calculate t0 margin requirement
199
200 Real initialBalance = 0.0;
201 if (balance && balance->variationMargin() != Null<Real>()) {
202 initialBalance = balance->variationMargin();
203 DLOG("initial collateral balance: " << initialBalance);
204 }
205 else {
206 DLOG("initial collateral balance not found");
207 }
208
209 QuantLib::ext::shared_ptr<CollateralAccount> tmpAcc(new CollateralAccount(csaDef, initialBalance, date_t0));
210 DLOG("tmp initial collateral balance: " << tmpAcc->balance_t0());
211 DLOG("tmp current collateral balance: " << tmpAcc->accountBalance());
212
213 Real bal_t0 = marginRequirementCalc(tmpAcc, nettingSetPv, date_t0);
214
215 // step 2; build a new collateral account object with t0 balance = bal_t0
216 // a copy of this new object will be used as base for each scenario collateral path
217 CollateralAccount baseAcc(csaDef, bal_t0, date_t0);
218 DLOG("base current collateral balance: " << bal_t0 << ", " << baseAcc.accountBalance());
219
220 // step 3; build an empty container for the return value(s)
221 QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>> scenarioCollatPaths(
222 new vector<QuantLib::ext::shared_ptr<CollateralAccount>>());
223
224 // step 4; start loop over scenarios
225 Size numScenarios = nettingSetValues.front().size();
226 QL_REQUIRE(numScenarios == csaFxScenarioRates.front().size(), "netting values -v- scenario FX rate mismatch");
227 Date simEndDate = std::min(nettingSet_maturity, dateGrid.back()) + csaDef->csaDetails()->marginPeriodOfRisk();
228 for (unsigned i = 0; i < numScenarios; i++) {
229 QuantLib::ext::shared_ptr<CollateralAccount> collat(new CollateralAccount(baseAcc));
230 Date tmpDate = date_t0; // the date which gets evolved
231 Date nextMarginReqDateUs = date_t0;
232 Date nextMarginReqDateCtp = date_t0;
233 while (tmpDate <= simEndDate) {
234 QL_REQUIRE(tmpDate <= nextMarginReqDateUs && tmpDate <= nextMarginReqDateCtp &&
235 (tmpDate == nextMarginReqDateUs || tmpDate == nextMarginReqDateCtp),
236 "collateral balance path generation error; invalid time stepping");
237 bool eligMarginReqDateUs = tmpDate == nextMarginReqDateUs ? true : false;
238 bool eligMarginReqDateCtp = tmpDate == nextMarginReqDateCtp ? true : false;
239 Real uncollatVal = estimateUncollatValue(tmpDate, nettingSetPv, date_t0, nettingSetValues, i, dateGrid);
240 Real fxValue = estimateUncollatValue(tmpDate, csaFxTodayRate, date_t0, csaFxScenarioRates, i, dateGrid);
241 Real annualisedZeroRate =
242 estimateUncollatValue(tmpDate, csaTodayCollatCurve, date_t0, csaScenCollatCurves, i, dateGrid);
243 uncollatVal /= fxValue;
244 updateMarginCall(collat, uncollatVal, tmpDate, annualisedZeroRate, calcType, eligMarginReqDateUs,
245 eligMarginReqDateCtp);
246
247 if (nextMarginReqDateUs == tmpDate)
248 nextMarginReqDateUs = tmpDate + collat->csaDef()->csaDetails()->marginCallFrequency();
249 if (nextMarginReqDateCtp == tmpDate)
250 nextMarginReqDateCtp = tmpDate + collat->csaDef()->csaDetails()->marginPostFrequency();
251 tmpDate = std::min(nextMarginReqDateUs, nextMarginReqDateCtp);
252 }
253 QL_REQUIRE(tmpDate > simEndDate,
254 "collateral balance path generation error; while loop terminated too early. ("
255 << tmpDate << ", " << simEndDate << ")");
256 // set account balance to zero after maturity of portfolio
257 collat->closeAccount(simEndDate + Period(1, Days));
258 scenarioCollatPaths->push_back(collat);
259 }
260 return scenarioCollatPaths;
261 } catch (const std::exception& e) {
262 QL_FAIL(e.what());
263 } catch (...) {
264 QL_FAIL("CollateralExposureHelper - unknown error when generating collateralBalancePaths");
265 }
266}
static void updateMarginCall(const QuantLib::ext::shared_ptr< CollateralAccount > &collat, const Real &uncollatValue, const Date &simulationDate, const Real &accrualFactor, const CalculationType &calcType=Symmetric, const bool &eligMarginReqDateUs=true, const bool &eligMarginReqDateCtp=true)
static Real estimateUncollatValue(const Date &simulationDate, const Real &npv_t0, const Date &date_t0, const vector< vector< T > > &scenPvProfiles, const unsigned &scenIndex, const vector< Date > &dateGrid)
#define DLOG(text)
+ Here is the call graph for this function:
+ Here is the caller graph for this function: