Skip to content

Commit dff8e0b

Browse files
rolandlichtersjenkins
authored andcommitted
Merge branch 'QPR-12228' into 'master'
Resolve QPR-12228 configurable dim distribution report Closes QPR-12228 See merge request qs/oreplus!2671
1 parent 1d1fc33 commit dff8e0b

9 files changed

Lines changed: 78 additions & 20 deletions

File tree

Examples/InitialMargin/Input/Dim2/ore_amccg.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@
104104
<Parameter name="dimRegressionFiles">dim_regression.csv</Parameter>
105105
<Parameter name="dimOutputNettingSet">CPTY_A</Parameter>
106106
<Parameter name="dimOutputGridPoints">0</Parameter>
107+
<!-- cover n std devs around mean of the data in dim distribution output, or "inf" to cover all of the data -->
108+
<Parameter name="dimDistributionCoveredStdDevs">5.0</Parameter>
109+
<!-- grid size for dim distribution report -->
110+
<Parameter name="dimDistributionGridSize">50</Parameter>
107111
<Parameter name="dimLocalRegressionEvaluations">0</Parameter>
108112
<Parameter name="dimLocalRegressionBandwidth">1.0</Parameter>
109113
<!-- <Parameter name="rawCubeOutputFile">rawcube.csv</Parameter> -->

OREAnalytics/orea/aggregation/dimcalculator.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,25 +164,24 @@ void DynamicInitialMarginCalculator::exportDimEvolution(ore::data::Report& dimEv
164164
LOG("Exporting expected DIM through time done");
165165
}
166166

167-
void DynamicInitialMarginCalculator::exportDimDistribution(ore::data::Report& dimDistributionReport) const {
167+
void DynamicInitialMarginCalculator::exportDimDistribution(ore::data::Report& dimDistributionReport,
168+
const Size gridSize, const Real coveredStdDevs) const {
168169

169170
dimDistributionReport.addColumn("NettingSet", string())
170171
.addColumn("TimeStep", Size())
171172
.addColumn("Date", Date())
172173
.addColumn("Bound", Real(), 6)
173174
.addColumn("Count", Size());
174175

175-
constexpr Size steps = 50;
176-
177176
std::vector<Real> bounds;
178177
std::vector<Size> counts;
179178

180179
for (const auto& [nettingSet, _] : dimCube_->idsAndIndexes()) {
181180

182181
for (Size i = 0; i < datesLoopSize_; ++i) {
183182
distributionCount(nettingSetDIM_.at(nettingSet).at(i).begin(), nettingSetDIM_.at(nettingSet).at(i).end(),
184-
steps, bounds, counts);
185-
for (Size j = 0; j < steps; ++j)
183+
gridSize, bounds, counts, coveredStdDevs);
184+
for (Size j = 0; j < gridSize; ++j)
186185
dimDistributionReport.next()
187186
.add(nettingSet)
188187
.add(i)
@@ -193,7 +192,6 @@ void DynamicInitialMarginCalculator::exportDimDistribution(ore::data::Report& di
193192
}
194193

195194
dimDistributionReport.end();
196-
197195
}
198196

199197
} // namespace analytics

OREAnalytics/orea/aggregation/dimcalculator.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
#pragma once
2525

26-
#include <orea/app/inputparameters.hpp>
2726
#include <orea/aggregation/collatexposurehelper.hpp>
27+
#include <orea/app/inputparameters.hpp>
2828
#include <orea/cube/cubeinterpretation.hpp>
2929
#include <orea/cube/inmemorycube.hpp>
3030
#include <orea/scenario/aggregationscenariodata.hpp>
@@ -67,8 +67,8 @@ class DynamicInitialMarginCalculator {
6767
Real quantile = 0.99,
6868
//! VaR holding period in calendar days
6969
Size horizonCalendarDays = 14,
70-
//! Actual t0 IM by netting set used to scale the DIM evolution, no scaling if the argument is omitted
71-
const std::map<std::string, Real>& currentIM = std::map<std::string, Real>());
70+
//! Actual t0 IM by netting set used to scale the DIM evolution, no scaling if the argument is omitted
71+
const std::map<std::string, Real>& currentIM = std::map<std::string, Real>());
7272

7373
virtual ~DynamicInitialMarginCalculator() {}
7474

@@ -85,7 +85,8 @@ class DynamicInitialMarginCalculator {
8585
virtual void exportDimEvolution(ore::data::Report& dimEvolutionReport) const;
8686

8787
//! DIM distribution report
88-
virtual void exportDimDistribution(ore::data::Report& dimDistributionReport) const;
88+
virtual void exportDimDistribution(ore::data::Report& dimDistributionReport, const Size gridSize = 50,
89+
const Real coveredStdDevs = 5.0) const;
8990

9091
//! DIM by nettingSet, date, sample returned as a regular NPV cube
9192
const QuantLib::ext::shared_ptr<NPVCube>& dimCube() { return dimCube_; }

OREAnalytics/orea/aggregation/postprocess.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,8 +791,9 @@ Real PostProcess::nettingSetCollateralFloor(const string& nettingSetId) {
791791
return nettedExposureCalculator_->collateralFloor(nettingSetId);
792792
}
793793

794-
void PostProcess::exportDimDistribution(ore::data::Report& dimDistributionReport) {
795-
return dimCalculator_->exportDimDistribution(dimDistributionReport);
794+
void PostProcess::exportDimDistribution(ore::data::Report& dimDistributionReport, const Size gridSize,
795+
const Real coveredStdDevs) {
796+
return dimCalculator_->exportDimDistribution(dimDistributionReport, gridSize, coveredStdDevs);
796797
}
797798

798799
void PostProcess::exportDimEvolution(ore::data::Report& dimEvolutionReport) {

OREAnalytics/orea/aggregation/postprocess.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ class PostProcess {
322322
//! Return the dynamic initial margin cube (regression approach)
323323
//const QuantLib::ext::shared_ptr<NPVCube>& dimCube() { return dimCube_; }
324324
//! Write DIM distributions through time for all netting sets
325-
void exportDimDistribution(ore::data::Report& dimDistributionReport);
325+
void exportDimDistribution(ore::data::Report& dimDistributionReport, const Size gridSize = 50,
326+
const Real coveredStdDevs = Null<Real>());
326327
//! Write average (over samples) DIM evolution through time for all netting sets
327328
void exportDimEvolution(ore::data::Report& dimEvolutionReport);
328329
//! Write DIM as a function of sample netting set NPV for a given time step

OREAnalytics/orea/app/analytics/xvaanalytic.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,8 @@ void XvaAnalyticImpl::runAnalytic(const QuantLib::ext::shared_ptr<ore::data::InM
11661166

11671167
// Generate DIM distribution report
11681168
auto dimDistributionReport = QuantLib::ext::make_shared<InMemoryReport>(inputs_->reportBufferSize());
1169-
postProcess_->exportDimDistribution(*dimDistributionReport);
1169+
postProcess_->exportDimDistribution(*dimDistributionReport, inputs_->dimDistributionGridSize(),
1170+
inputs_->dimDistributionCoveredStdDevs());
11701171
analytic()->addReport(LABEL, "dim_distribution", dimDistributionReport);
11711172

11721173
// Generate DIM regression reports

OREAnalytics/orea/app/inputparameters.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ class InputParameters {
339339
void setDimRegressionOrder(Size s) { dimRegressionOrder_ = s; }
340340
void setDimRegressors(const std::string& s); // parse to vector<string>
341341
void setDimOutputGridPoints(const std::string& s); // parse to vector<Size>
342+
void setDimDistributionCoveredStdDevs(Real r) { dimDistributionCoveredStdDevs_ = r; }
343+
void setDimDistributionGridSize(Size n) { dimDistributionGridSize_ = n; }
342344
void setDimOutputNettingSet(const std::string& s) { dimOutputNettingSet_ = s; }
343345
void setDimLocalRegressionEvaluations(Size s) { dimLocalRegressionEvaluations_ = s; }
344346
void setDimLocalRegressionBandwidth(Real r) { dimLocalRegressionBandwidth_ = r; }
@@ -761,6 +763,8 @@ class InputParameters {
761763
Size dimRegressionOrder() const { return dimRegressionOrder_; }
762764
const std::vector<std::string>& dimRegressors() const { return dimRegressors_; }
763765
const std::vector<Size>& dimOutputGridPoints() const { return dimOutputGridPoints_; }
766+
Real dimDistributionCoveredStdDevs() const { return dimDistributionCoveredStdDevs_; }
767+
Size dimDistributionGridSize() const { return dimDistributionGridSize_; }
764768
const std::string& dimOutputNettingSet() const { return dimOutputNettingSet_; }
765769
Size dimLocalRegressionEvaluations() const { return dimLocalRegressionEvaluations_; }
766770
Real dimLocalRegressionBandwidth() const { return dimLocalRegressionBandwidth_; }
@@ -1164,6 +1168,8 @@ class InputParameters {
11641168
Size dimRegressionOrder_ = 0;
11651169
vector<string> dimRegressors_;
11661170
vector<Size> dimOutputGridPoints_;
1171+
Real dimDistributionCoveredStdDevs_ = 5.0;
1172+
Size dimDistributionGridSize_ = 50;
11671173
string dimOutputNettingSet_;
11681174
Size dimLocalRegressionEvaluations_ = 0;
11691175
Real dimLocalRegressionBandwidth_ = 0.25;

OREAnalytics/orea/app/oreapp.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,6 +2103,14 @@ void OREAppInputParameters::loadParameters() {
21032103
if (tmp != "")
21042104
setDimOutputGridPoints(tmp);
21052105

2106+
tmp = params_->get("pfe", "dimDistributionCoveredStdDevs", false);
2107+
if (tmp != "")
2108+
setDimDistributionCoveredStdDevs(tmp == "inf" ? Null<Real>() : parseReal(tmp));
2109+
2110+
tmp = params_->get("pfe", "dimDistributionGridSize", false);
2111+
if (tmp != "")
2112+
setDimDistributionGridSize(parseInteger(tmp));
2113+
21062114
tmp = params_->get("pfe", "dimOutputNettingSet", false);
21072115
if (tmp != "")
21082116
setDimOutputNettingSet(tmp);
@@ -2418,6 +2426,14 @@ void OREAppInputParameters::loadParameters() {
24182426
if (tmp != "")
24192427
setDimOutputGridPoints(tmp);
24202428

2429+
tmp = params_->get("xva", "dimDistributionCoveredStdDevs", false);
2430+
if (tmp != "")
2431+
setDimDistributionCoveredStdDevs(tmp == "inf" ? Null<Real>() : parseReal(tmp));
2432+
2433+
tmp = params_->get("xva", "dimDistributionGridSize", false);
2434+
if (tmp != "")
2435+
setDimDistributionGridSize(parseInteger(tmp));
2436+
24212437
tmp = params_->get("xva", "dimOutputNettingSet", false);
24222438
if (tmp != "")
24232439
setDimOutputNettingSet(tmp);

QuantExt/qle/math/distributioncount.hpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,52 @@
2323

2424
#pragma once
2525

26+
#include <boost/accumulators/accumulators.hpp>
27+
#include <boost/accumulators/statistics/mean.hpp>
28+
#include <boost/accumulators/statistics/stats.hpp>
29+
#include <boost/accumulators/statistics/variance.hpp>
30+
2631
namespace QuantExt {
2732

2833
template <class I>
29-
void distributionCount(I begin, I end, const Size steps, std::vector<Real>& bounds, std::vector<Size>& counts) {
30-
Real xmin = *std::min_element(begin, end);
31-
Real xmax = *std::max_element(begin, end);
34+
void distributionCount(I begin, I end, const Size steps, std::vector<Real>& bounds, std::vector<Size>& counts,
35+
const Real coveredStdDevs = Null<Real>()) {
36+
3237
std::vector<Real> v(begin, end);
3338
std::sort(v.begin(), v.end());
39+
40+
Real xmin, xmax;
41+
if (coveredStdDevs == Null<Real>()) {
42+
43+
// cover [xmin, xmax] in output
44+
45+
xmin = *std::min_element(begin, end);
46+
xmax = *std::max_element(begin, end);
47+
48+
} else {
49+
50+
// cover given number of std devs around mean in output
51+
52+
boost::accumulators::accumulator_set<
53+
double, boost::accumulators::stats<boost::accumulators::tag::mean, boost::accumulators::tag::variance>>
54+
acc;
55+
std::for_each(v.begin(), v.end(), [&acc](double x) { acc(x); });
56+
double mean = boost::accumulators::mean(acc);
57+
double stdDev = std::sqrt(boost::accumulators::variance(acc));
58+
xmin = mean - coveredStdDevs * stdDev;
59+
xmax = mean + coveredStdDevs * stdDev;
60+
}
61+
3462
Real h = (xmax - xmin) / static_cast<Real>(steps);
35-
Size idx0 = 0;
63+
Size idx0 = coveredStdDevs == Null<Real>() ? 0 : std::upper_bound(v.begin(), v.end(), xmin) - v.begin();
3664
counts.resize(steps);
3765
bounds.resize(steps);
3866
for (Size i = 0; i < steps; ++i) {
3967
Real v1 = xmin + static_cast<Real>(i + 1) * h;
40-
// the code for "i == steps - 1" ensures all observations are accounted for
41-
Size idx1 = i == steps - 1 ? v.size() : std::upper_bound(v.begin(), v.end(), v1) - v.begin();
68+
// if all data should be covered, ensure the counts sum up to v.size()
69+
Size idx1 = coveredStdDevs == Null<Real>() && i == steps - 1
70+
? v.size()
71+
: std::upper_bound(v.begin(), v.end(), v1) - v.begin();
4272
counts[i] = idx1 - idx0;
4373
bounds[i] = v1;
4474
idx0 = idx1;

0 commit comments

Comments
 (0)