20#include <ql/errors.hpp>
25#define FLAT_INTERPOLATION 1
32 static map<string, CollateralExposureHelper::CalculationType> m = {
42 QL_FAIL(
"Collateral Calculation Type \"" << s <<
"\" not recognized");
50 out <<
"AsymmetricCVA";
52 out <<
"AsymmetricDVA";
56 QL_FAIL(
"Collateral calculation type not covered");
61 const Real& uncollatValue,
const Date& simulationDate) {
65 Real collatBalance = collat->accountBalance();
68 Real openMargins = collat->outstandingMarginAmount(simulationDate);
69 Real collatShortfall = csa - collatBalance - openMargins;
72 if (collatShortfall >= 0.0)
73 mta = collat->csaDef()->csaDetails()->mtaRcv();
75 mta = collat->csaDef()->csaDetails()->mtaPay();
77 Real deliveryAmount = fabs(collatShortfall) >= mta ? (collatShortfall) : 0.0;
79 return deliveryAmount;
83 const QuantLib::ext::shared_ptr<ore::data::NettingSetDefinition>& nettingSet,
84 const Real& uncollatValueCsaCur) {
86 Real ia = nettingSet->csaDetails()->independentAmountHeld();
88 if (uncollatValueCsaCur + ia >= 0) {
89 threshold = nettingSet->csaDetails()->thresholdRcv();
90 csa =
max(uncollatValueCsaCur + ia - threshold, 0.0);
93 threshold = nettingSet->csaDetails()->thresholdPay();
95 csa =
min(uncollatValueCsaCur + ia + threshold, 0.0);
102 const Date& date_t0,
const vector<vector<T>>& scenPvProfiles,
103 const unsigned& scenIndex,
const vector<Date>& dateGrid) {
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");
108 if (simulationDate >= dateGrid.back())
109 return scenPvProfiles.back()[scenIndex];
110 if (simulationDate == date_t0)
112 for (
unsigned i = 0; i < dateGrid.size(); i++) {
113 if (dateGrid[i] == simulationDate)
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];
127 if (simulationDate <= dateGrid[0]) {
131 npv2 = scenPvProfiles[0][scenIndex];
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();
142 npv1 = scenPvProfiles[pos1][scenIndex];
143 npv2 = scenPvProfiles[pos2][scenIndex];
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);
156 const Real& uncollatValue,
const Date& simulationDate,
158 const bool& eligMarginReqDateUs,
const bool& eligMarginReqDateCtp) {
159 collat->updateAccountBalance(simulationDate, annualisedZeroRate);
175 Period lag = (calcType ==
NoLag ? 0*Days : collat->csaDef()->csaDetails()->marginPeriodOfRisk());
176 if (margin > 0.0 && eligMarginReqDateUs) {
178 (calcType ==
AsymmetricDVA ? simulationDate : simulationDate + lag);
179 collat->updateMarginCall(margin, marginPayDate, simulationDate);
180 }
else if (margin < 0.0 && eligMarginReqDateCtp) {
182 (calcType ==
AsymmetricCVA ? simulationDate : simulationDate + lag);
183 collat->updateMarginCall(margin, marginPayDate, simulationDate);
190 const QuantLib::ext::shared_ptr<NettingSetDefinition>& csaDef,
const Real& nettingSetPv,
const Date& date_t0,
191 const vector<vector<Real>>& nettingSetValues,
const Date& nettingSet_maturity,
const vector<Date>& dateGrid,
192 const Real& csaFxTodayRate,
const vector<vector<Real>>& csaFxScenarioRates,
const Real& csaTodayCollatCurve,
193 const vector<vector<Real>>& csaScenCollatCurves,
const CalculationType& calcType,
194 const QuantLib::ext::shared_ptr<CollateralBalance>& balance) {
200 Real initialBalance = 0.0;
201 if (balance && balance->variationMargin() != Null<Real>()) {
202 initialBalance = balance->variationMargin();
203 DLOG(
"initial collateral balance: " << initialBalance);
206 DLOG(
"initial collateral balance not found");
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());
218 DLOG(
"base current collateral balance: " << bal_t0 <<
", " << baseAcc.
accountBalance());
221 QuantLib::ext::shared_ptr<vector<QuantLib::ext::shared_ptr<CollateralAccount>>> scenarioCollatPaths(
222 new vector<QuantLib::ext::shared_ptr<CollateralAccount>>());
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;
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 =
243 uncollatVal /= fxValue;
244 updateMarginCall(collat, uncollatVal, tmpDate, annualisedZeroRate, calcType, eligMarginReqDateUs,
245 eligMarginReqDateCtp);
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);
253 QL_REQUIRE(tmpDate > simEndDate,
254 "collateral balance path generation error; while loop terminated too early. ("
255 << tmpDate <<
", " << simEndDate <<
")");
257 collat->closeAccount(simEndDate + Period(1, Days));
258 scenarioCollatPaths->push_back(collat);
260 return scenarioCollatPaths;
261 }
catch (
const std::exception& e) {
264 QL_FAIL(
"CollateralExposureHelper - unknown error when generating collateralBalancePaths");
Real accountBalance() const
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 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 Real marginRequirementCalc(const QuantLib::ext::shared_ptr< CollateralAccount > &collat, const Real &uncollatValue, const Date &simulationDate)
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 >())
Collateral Exposure Helper Functions (stored in base currency)
RandomVariable max(RandomVariable x, const RandomVariable &y)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
RandomVariable min(RandomVariable x, const RandomVariable &y)
CollateralExposureHelper::CalculationType parseCollateralCalculationType(const string &s)
Convert text representation to CollateralExposureHelper::CalculationType.