25#include <boost/math/distributions/normal.hpp>
31#include <ql/math/comparison.hpp>
32#include <ql/quote.hpp>
51using QuantLib::close_enough;
66 const QuantLib::ext::shared_ptr<SimmConfiguration>& simmConfiguration,
67 const string& calculationCcyCall,
const string& calculationCcyPost,
68 const string& resultCcy,
const QuantLib::ext::shared_ptr<Market> market,
69 const bool determineWinningRegulations,
const bool enforceIMRegulations,
70 const bool quiet,
const map<
SimmSide, set<NettingSetDetails>>& hasSEC,
71 const map<
SimmSide, set<NettingSetDetails>>& hasCFTC)
72 : simmConfiguration_(simmConfiguration), calculationCcyCall_(calculationCcyCall),
73 calculationCcyPost_(calculationCcyPost), resultCcy_(resultCcy.empty() ? calculationCcyCall_ : resultCcy),
74 market_(market), quiet_(quiet), hasSEC_(hasSEC), hasCFTC_(hasCFTC) {
81 "SIMM Calculator: The result currency (" <<
resultCcy_ <<
") must be a valid ISO currency code");
85 if (cr.riskType == CrifRecord::RiskType::Empty) {
89 const bool isSchedule = cr.imModel ==
"Schedule";
91 if (!
quiet_ && determineWinningRegulations) {
102 }
else if (
collectRegsIsEmpty_.at(cr.nettingSetDetails) && !cr.collectRegulations.empty()) {
107 }
else if (
postRegsIsEmpty_.at(cr.nettingSetDetails) && !cr.postRegulations.empty()) {
114 if (cr.requiresAmountUsd() &&
resultCcy_ ==
"USD" && cr.hasAmountUsd()) {
116 }
else if(cr.requiresAmountUsd()) {
133 LOG(
"SimmCalculator: Splitting up original CRIF records into their respective collect/post regulations");
140 for (
auto& [nettingDetails, regulationCrifMap] : nettingsSetCrifMap) {
143 const bool hasCFTCGlobal =
hasCFTC_[side].find(nettingDetails) !=
hasCFTC_[side].end();
144 const bool hasSECGlobal =
hasSEC_[side].find(nettingDetails) !=
hasSEC_[side].end();
145 const bool hasSECLocal = regulationCrifMap.find(
"SEC") != regulationCrifMap.end();
146 const bool hasCFTCLocal = regulationCrifMap.find(
"CFTC") != regulationCrifMap.end();
148 if ((hasSECLocal && hasCFTCLocal) || (hasCFTCGlobal && hasSECGlobal)) {
153 regulationCrifMap[
"SEC"] =
Crif();
159 const auto& crifCFTC = regulationCrifMap[
"CFTC"];
160 const auto& crifSEC = regulationCrifMap[
"SEC"];
161 for (
const auto& cr :crifCFTC) {
164 if (crifSEC.find(cr) == crifSEC.end()) {
166 DLOG(
"SimmCalculator: Inserting CRIF record with CFTC "
167 << nettingDetails <<
" regulation into SEC CRIF records: " << cr);
169 regulationCrifMap[
"SEC"].addRecord(cr);
176 for (
auto& [regulation, crif] : regulationCrifMap) {
183 if (regulationCrifMap.count(
"Unspecified") > 0 && regulationCrifMap.size() > 1)
184 regulationCrifMap.erase(
"Unspecified");
190 for (
const auto& [nsd, regulationCrifMap] : nettingSetRegulationCrifMap) {
192 for (
const auto& [regulation, crif] : regulationCrifMap) {
193 bool hasFixedAddOn =
false;
194 for (
const auto& sp : crif) {
195 if (sp.riskType == RiskType::AddOnFixedAmount) {
196 hasFixedAddOn =
true;
200 if (crif.hasCrifRecords() || hasFixedAddOn)
207 if (determineWinningRegulations) {
209 LOG(
"SimmCalculator: Determining winning regulations");
216 for (
const auto& kv : sv.second) {
219 Real winningMargin = std::numeric_limits<Real>::min();
220 map<string, Real> nettingSetMargins;
221 std::vector<Real> margins;
222 for (
const auto& regSimmResults : kv.second) {
223 const Real& im = regSimmResults.second.get(ProductClass::All, RiskClass::All, MarginType::All,
"All");
224 nettingSetMargins[regSimmResults.first] = im;
225 if (im > winningMargin)
231 for (
const auto& kv : nettingSetMargins) {
256 LOG(
"SimmCalculator: Calculating SIMM " << side <<
" for portfolio [" << nettingSetDetails <<
"], regulation "
262 LOG(
"SimmCalculator: Calculating SIMM for product class " << productClass);
268 auto p =
irDeltaMargin(nettingSetDetails, productClass, crif, side);
270 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
273 p =
margin(nettingSetDetails, productClass, RiskType::FX, crif, side);
275 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
277 rc = RiskClass::CreditQualifying;
278 p =
margin(nettingSetDetails, productClass, RiskType::CreditQ, crif, side);
280 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
282 rc = RiskClass::CreditNonQualifying;
283 p =
margin(nettingSetDetails, productClass, RiskType::CreditNonQ, crif, side);
285 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
287 rc = RiskClass::Equity;
288 p =
margin(nettingSetDetails, productClass, RiskType::Equity, crif, side);
290 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
292 rc = RiskClass::Commodity;
293 p =
margin(nettingSetDetails, productClass, RiskType::Commodity, crif, side);
295 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
298 mt = MarginType::Vega;
299 rc = RiskClass::InterestRate;
300 p =
irVegaMargin(nettingSetDetails, productClass, crif, side);
302 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
305 p =
margin(nettingSetDetails, productClass, RiskType::FXVol, crif, side);
307 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
309 rc = RiskClass::CreditQualifying;
310 p =
margin(nettingSetDetails, productClass, RiskType::CreditVol, crif, side);
312 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
314 rc = RiskClass::CreditNonQualifying;
315 p =
margin(nettingSetDetails, productClass, RiskType::CreditVolNonQ, crif, side);
317 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
319 rc = RiskClass::Equity;
320 p =
margin(nettingSetDetails, productClass, RiskType::EquityVol, crif, side);
322 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
324 rc = RiskClass::Commodity;
325 p =
margin(nettingSetDetails, productClass, RiskType::CommodityVol, crif, side);
327 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
330 mt = MarginType::Curvature;
331 rc = RiskClass::InterestRate;
335 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
338 p =
curvatureMargin(nettingSetDetails, productClass, RiskType::FXVol, side, crif,
false);
340 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
342 rc = RiskClass::CreditQualifying;
343 p =
curvatureMargin(nettingSetDetails, productClass, RiskType::CreditVol, side, crif);
345 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
347 rc = RiskClass::CreditNonQualifying;
348 p =
curvatureMargin(nettingSetDetails, productClass, RiskType::CreditVolNonQ, side, crif);
350 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
352 rc = RiskClass::Equity;
353 p =
curvatureMargin(nettingSetDetails, productClass, RiskType::EquityVol, side, crif,
false);
355 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
357 rc = RiskClass::Commodity;
358 p =
curvatureMargin(nettingSetDetails, productClass, RiskType::CommodityVol, side, crif,
false);
360 add(nettingSetDetails, regulation, productClass, rc, mt, p.first, side);
365 p =
margin(nettingSetDetails, productClass, RiskType::BaseCorr, crif, side);
367 add(nettingSetDetails, regulation, productClass, RiskClass::CreditQualifying, MarginType::BaseCorr,
381 QL_REQUIRE(subWinningRegs.find(nettingSetDetails) != subWinningRegs.end(),
382 "SimmCalculator::winningRegulations(): Could not find netting set in the list of "
383 << side <<
" IM winning regulations: " << nettingSetDetails);
384 return subWinningRegs.at(nettingSetDetails);
389 "SimmCalculator::winningRegulations(): Could not find list of" << side <<
" IM winning regulations");
398 const string& regulation)
const {
399 const auto& subResults =
simmResults(side, nettingSetDetails);
400 QL_REQUIRE(subResults.find(regulation) != subResults.end(),
401 "SimmCalculator::simmResults(): Could not find regulation in the SIMM "
402 << side <<
" results for netting set [" << nettingSetDetails <<
"]: " << regulation);
403 return subResults.at(regulation);
409 QL_REQUIRE(subResults.find(nettingSetDetails) != subResults.end(),
410 "SimmCalculator::simmResults(): Could not find netting set in the SIMM "
411 << side <<
" results: " << nettingSetDetails);
412 return subResults.at(nettingSetDetails);
417 "SimmCalculator::simmResults(): Could not find " << side <<
" IM in the SIMM results");
428 QL_REQUIRE(subResults.find(nettingSetDetails) != subResults.end(),
429 "SimmCalculator::finalSimmResults(): Could not find netting set in the final SIMM "
430 << side <<
" results: " << nettingSetDetails);
431 return subResults.at(nettingSetDetails);
436 "SimmCalculator::finalSimmResults(): Could not find " << side <<
" IM in the final SIMM results");
450 map<string, Real> bucketMargins;
453 set<string> qualifiers =
454 getQualifiers(crif, nettingSetDetails, pc, {RiskType::IRCurve, RiskType::XCcyBasis, RiskType::Inflation});
457 if (qualifiers.empty()) {
458 bucketMargins[
"All"] = 0.0;
459 return make_pair(bucketMargins,
false);
463 map<string, Real> concentrationRisk;
465 map<string, Real> deltaMargin;
467 map<string, Real> sumWeightedSensis;
471 for (
const auto& qualifier : qualifiers) {
477 auto XccyCount = crif.
countMatching(nettingSetDetails, pc, RiskType::XCcyBasis, qualifier);
478 QL_REQUIRE(XccyCount < 2,
"SIMM Calcuator: Expected either 0 or 1 elements for risk type "
479 << RiskType::XCcyBasis <<
" and qualifier " << qualifier <<
" but got "
481 auto itXccy = crif.
findBy(nettingSetDetails, pc, RiskType::XCcyBasis, qualifier);
484 auto inflationCount = crif.
countMatching(nettingSetDetails, pc, RiskType::Inflation, qualifier);
485 QL_REQUIRE(inflationCount < 2,
"SIMM Calculator: Expected either 0 or 1 elements for risk type "
486 << RiskType::Inflation <<
" and qualifier " << qualifier <<
" but got "
488 auto itInflation = crif.
findBy(nettingSetDetails, pc, RiskType::Inflation, qualifier);
493 for (
const auto& it : pIrQualifier) {
494 concentrationRisk[qualifier] += it.amountResultCcy;
497 if (itInflation != crif.
end()){
498 concentrationRisk[qualifier] += itInflation->amountResultCcy;
501 Real concThreshold =
simmConfiguration_->concentrationThreshold(RiskType::IRCurve, qualifier);
504 concentrationRisk[qualifier] /= concThreshold;
506 concentrationRisk[qualifier] =
max(1.0,
sqrt(std::abs(concentrationRisk[qualifier])));
509 for (
auto itOuter = pIrQualifier.begin(); itOuter != pIrQualifier.end(); ++itOuter) {
511 Real rwOuter =
simmConfiguration_->weight(RiskType::IRCurve, qualifier, itOuter->label1);
513 Real wsOuter = rwOuter * itOuter->amountResultCcy * concentrationRisk[qualifier];
515 sumWeightedSensis[qualifier] += wsOuter;
517 deltaMargin[qualifier] += wsOuter * wsOuter;
519 for (
auto itInner = pIrQualifier.begin(); itInner != itOuter; ++itInner) {
521 Real subCurveCorr =
simmConfiguration_->correlation(RiskType::IRCurve, qualifier,
"", itOuter->label2,
522 RiskType::IRCurve, qualifier,
"", itInner->label2);
524 Real tenorCorr =
simmConfiguration_->correlation(RiskType::IRCurve, qualifier, itOuter->label1,
"",
525 RiskType::IRCurve, qualifier, itInner->label1,
"");
527 Real rwInner =
simmConfiguration_->weight(RiskType::IRCurve, qualifier, itInner->label1);
528 Real wsInner = rwInner * itInner->amountResultCcy * concentrationRisk[qualifier];
529 deltaMargin[qualifier] += 2 * subCurveCorr * tenorCorr * wsOuter * wsInner;
534 Real wsInflation = 0.0;
535 if (itInflation != crif.
end()) {
537 Real rwInflation =
simmConfiguration_->weight(RiskType::Inflation, qualifier, itInflation->label1);
539 wsInflation = rwInflation * itInflation->amountResultCcy * concentrationRisk[qualifier];
541 sumWeightedSensis[qualifier] += wsInflation;
543 deltaMargin[qualifier] += wsInflation * wsInflation;
546 Real corr =
simmConfiguration_->correlation(RiskType::IRCurve, qualifier,
"",
"", RiskType::Inflation,
548 for (
auto it = pIrQualifier.begin(); it != pIrQualifier.end(); ++it) {
551 Real ws = rw * it->amountResultCcy * concentrationRisk[qualifier];
552 deltaMargin[qualifier] += 2 * corr * ws * wsInflation;
557 if (itXccy != crif.
end()) {
559 Real rwXccy =
simmConfiguration_->weight(RiskType::XCcyBasis, qualifier, itXccy->label1);
561 Real wsXccy = rwXccy * itXccy->amountResultCcy;
563 sumWeightedSensis[qualifier] += wsXccy;
565 deltaMargin[qualifier] += wsXccy * wsXccy;
568 Real corr =
simmConfiguration_->correlation(RiskType::IRCurve, qualifier,
"",
"", RiskType::XCcyBasis,
570 for (
auto it = pIrQualifier.begin(); it != pIrQualifier.end(); ++it) {
572 Real rw =
simmConfiguration_->weight(RiskType::IRCurve, qualifier, it->label1, calcCcy);
573 Real ws = rw * it->amountResultCcy * concentrationRisk[qualifier];
574 deltaMargin[qualifier] += 2 * corr * ws * wsXccy;
578 if (itInflation != crif.
end()) {
580 Real corr =
simmConfiguration_->correlation(RiskType::Inflation, qualifier,
"",
"", RiskType::XCcyBasis,
582 deltaMargin[qualifier] += 2 * corr * wsInflation * wsXccy;
587 deltaMargin[qualifier] =
sqrt(
max(deltaMargin[qualifier], 0.0));
592 for (
auto itOuter = qualifiers.begin(); itOuter != qualifiers.end(); ++itOuter) {
594 margin += deltaMargin.at(*itOuter) * deltaMargin.at(*itOuter);
596 Real sOuter =
max(
min(sumWeightedSensis.at(*itOuter), deltaMargin.at(*itOuter)), -deltaMargin.at(*itOuter));
597 for (
auto itInner = qualifiers.begin(); itInner != itOuter; ++itInner) {
598 Real sInner =
max(
min(sumWeightedSensis.at(*itInner), deltaMargin.at(*itInner)), -deltaMargin.at(*itInner));
599 Real g =
min(concentrationRisk.at(*itOuter), concentrationRisk.at(*itInner)) /
600 max(concentrationRisk.at(*itOuter), concentrationRisk.at(*itInner));
601 Real corr =
simmConfiguration_->correlation(RiskType::IRCurve, *itOuter,
"",
"", RiskType::IRCurve,
603 margin += 2.0 * sOuter * sInner * corr * g;
608 for (
const auto& m : deltaMargin)
609 bucketMargins[m.first] = m.second;
610 bucketMargins[
"All"] =
margin;
612 return make_pair(bucketMargins,
true);
622 map<string, Real> bucketMargins;
625 set<string> qualifiers =
getQualifiers(crif, nettingSetDetails, pc, {RiskType::IRVol, RiskType::InflationVol});
628 if (qualifiers.empty()) {
629 bucketMargins[
"All"] = 0.0;
630 return make_pair(bucketMargins,
false);
634 map<string, Real> concentrationRisk;
636 map<string, Real> vegaMargin;
638 map<string, Real> sumWeightedSensis;
641 for (
const auto& qualifier : qualifiers) {
643 auto pIrQualifier = crif.
filterByQualifier(nettingSetDetails, pc, RiskType::IRVol, qualifier);
646 auto pInfQualifier = crif.
filterByQualifier(nettingSetDetails, pc, RiskType::InflationVol, qualifier);
649 for (
const auto& it : pIrQualifier) {
650 concentrationRisk[qualifier] += it.amountResultCcy;
652 for (
const auto& it : pInfQualifier) {
653 concentrationRisk[qualifier] += it.amountResultCcy;
656 Real concThreshold =
simmConfiguration_->concentrationThreshold(RiskType::IRVol, qualifier);
659 concentrationRisk[qualifier] /= concThreshold;
662 concentrationRisk[qualifier] =
max(1.0,
sqrt(std::abs(concentrationRisk[qualifier])));
666 for (
auto itOuter = pIrQualifier.begin(); itOuter != pIrQualifier.end(); ++itOuter) {
668 Real rwOuter =
simmConfiguration_->weight(RiskType::IRVol, qualifier, itOuter->label1);
670 Real wsOuter = rwOuter * itOuter->amountResultCcy * concentrationRisk[qualifier];
672 sumWeightedSensis[qualifier] += wsOuter;
674 vegaMargin[qualifier] += wsOuter * wsOuter;
676 for (
auto itInner = pIrQualifier.begin(); itInner != itOuter; ++itInner) {
678 Real corr =
simmConfiguration_->correlation(RiskType::IRVol, qualifier, itOuter->label1,
"",
679 RiskType::IRVol, qualifier, itInner->label1,
"");
681 Real rwInner =
simmConfiguration_->weight(RiskType::IRVol, qualifier, itInner->label1);
682 Real wsInner = rwInner * itInner->amountResultCcy * concentrationRisk[qualifier];
683 vegaMargin[qualifier] += 2 * corr * wsOuter * wsInner;
690 for (
auto itOuter = pInfQualifier.begin(); itOuter != pInfQualifier.end(); ++itOuter) {
692 Real rwOuter =
simmConfiguration_->weight(RiskType::InflationVol, qualifier, itOuter->label1);
694 Real wsOuter = rwOuter * itOuter->amountResultCcy * concentrationRisk[qualifier];
696 sumWeightedSensis[qualifier] += wsOuter;
698 vegaMargin[qualifier] += wsOuter * wsOuter;
701 for (
auto itInner = pIrQualifier.begin(); itInner != pIrQualifier.end(); ++itInner) {
703 Real corr =
simmConfiguration_->correlation(RiskType::InflationVol, qualifier, itOuter->label1,
"",
704 RiskType::IRVol, qualifier, itInner->label1,
"");
706 Real rwInner =
simmConfiguration_->weight(RiskType::IRVol, qualifier, itInner->label1);
707 Real wsInner = rwInner * itInner->amountResultCcy * concentrationRisk[qualifier];
708 vegaMargin[qualifier] += 2 * corr * wsOuter * wsInner;
711 for (
auto itInner = pInfQualifier.begin(); itInner != itOuter; ++itInner) {
713 Real corr =
simmConfiguration_->correlation(RiskType::InflationVol, qualifier, itOuter->label1,
"",
714 RiskType::InflationVol, qualifier, itInner->label1,
"");
716 Real rwInner =
simmConfiguration_->weight(RiskType::InflationVol, qualifier, itInner->label1);
717 Real wsInner = rwInner * itInner->amountResultCcy * concentrationRisk[qualifier];
718 vegaMargin[qualifier] += 2 * corr * wsOuter * wsInner;
723 vegaMargin[qualifier] =
sqrt(
max(vegaMargin[qualifier], 0.0));
728 for (
auto itOuter = qualifiers.begin(); itOuter != qualifiers.end(); ++itOuter) {
730 margin += vegaMargin.at(*itOuter) * vegaMargin.at(*itOuter);
732 Real sOuter =
max(
min(sumWeightedSensis.at(*itOuter), vegaMargin.at(*itOuter)), -vegaMargin.at(*itOuter));
733 for (
auto itInner = qualifiers.begin(); itInner != itOuter; ++itInner) {
734 Real sInner =
max(
min(sumWeightedSensis.at(*itInner), vegaMargin.at(*itInner)), -vegaMargin.at(*itInner));
735 Real g =
min(concentrationRisk.at(*itOuter), concentrationRisk.at(*itInner)) /
736 max(concentrationRisk.at(*itOuter), concentrationRisk.at(*itInner));
737 Real corr =
simmConfiguration_->correlation(RiskType::IRVol, *itOuter,
"",
"", RiskType::IRVol, *itInner,
739 margin += 2.0 * sOuter * sInner * corr * g;
744 for (
const auto& m : vegaMargin)
745 bucketMargins[m.first] = m.second;
746 bucketMargins[
"All"] =
margin;
748 return make_pair(bucketMargins,
true);
756 map<string, Real> bucketMargins;
759 Real multiplier = side == SimmSide::Call ? 1.0 : -1.0;
762 set<string> qualifiers =
getQualifiers(crif, nettingSetDetails, pc, {RiskType::IRVol, RiskType::InflationVol});
765 if (qualifiers.empty()) {
766 bucketMargins[
"All"] = 0.0;
767 return make_pair(bucketMargins,
false);
773 map<string, Real> sumWeightedSensis;
780 for (
const auto& qualifier : qualifiers) {
782 auto pIrQualifier = crif.
filterByQualifier(nettingSetDetails, pc, RiskType::IRVol, qualifier);
786 crif.
filterByQualifier(nettingSetDetails, pc, RiskType::InflationVol, qualifier);
790 for (
auto itOuter = pIrQualifier.begin(); itOuter != pIrQualifier.end(); ++itOuter) {
794 Real wsOuter = sfOuter * (itOuter->amountResultCcy * multiplier);
796 sumWeightedSensis[qualifier] += wsOuter;
798 sumAbsWs += std::abs(wsOuter);
802 for (
auto itInner = pIrQualifier.begin(); itInner != itOuter; ++itInner) {
804 Real corr =
simmConfiguration_->correlation(RiskType::IRVol, qualifier, itOuter->label1,
"",
805 RiskType::IRVol, qualifier, itInner->label1,
"");
808 Real wsInner = sfInner * (itInner->amountResultCcy * multiplier);
819 for (
auto infIt = pInfQualifier.begin(); infIt != pInfQualifier.end(); ++infIt) {
821 Real infSf =
simmConfiguration_->curvatureWeight(RiskType::InflationVol, infIt->label1);
822 infWs += infSf * (infIt->amountResultCcy * multiplier);
825 sumWeightedSensis[qualifier] += infWs;
827 sumAbsWs += std::abs(infWs);
834 for (
auto irIt = pIrQualifier.begin(); irIt != pIrQualifier.end(); ++irIt) {
836 Real corr =
simmConfiguration_->correlation(RiskType::InflationVol, qualifier,
"",
"", RiskType::IRVol,
837 qualifier, irIt->label1,
"");
840 Real irWs = irSf * (irIt->amountResultCcy * multiplier);
851 bucketMargins[
"All"] = 0.0;
852 return make_pair(bucketMargins,
true);
856 Real theta =
min(sumWs / sumAbsWs, 0.0);
859 for (
auto itOuter = qualifiers.begin(); itOuter != qualifiers.end(); ++itOuter) {
865 for (
auto itInner = qualifiers.begin(); itInner != itOuter; ++itInner) {
869 simmConfiguration_->correlation(RiskType::IRVol, *itOuter,
"",
"", RiskType::IRVol, *itInner,
"",
"");
870 margin += 2.0 * sOuter * sInner * corr * corr;
876 bucketMargins[m.first] = m.second;
879 Real totalCurvatureMargin = scaling *
max(
margin, 0.0);
881 bucketMargins[
"All"] = totalCurvatureMargin;
883 return make_pair(bucketMargins,
true);
896 map<string, Real> bucketMargins;
898 bool riskClassIsFX = rt == RiskType::FX || rt == RiskType::FXVol;
901 map<std::pair<std::string,std::string>, std::vector<CrifRecord>> crifByQualifierAndBucket;
902 map<std::string, std::vector<CrifRecord>> crifByBucket;
905 map<string, set<string>> buckets;
906 for(
const auto& it : crif.
filterBy(nettingSetDetails, pc, rt)) {
907 buckets[it.bucket].insert(it.qualifier);
908 crifByQualifierAndBucket[std::make_pair(it.qualifier,it.bucket)].push_back(it);
909 crifByBucket[it.bucket].push_back(it);
913 if (buckets.empty()) {
914 bucketMargins[
"All"] = 0.0;
915 return make_pair(bucketMargins,
false);
919 map<string, Real> bucketMargin;
921 map<string, Real> sumWeightedSensis;
926 for (
const auto& kv : buckets) {
927 string bucket = kv.first;
930 sumWeightedSensis[bucket] = 0.0;
933 map<string, Real> concentrationRisk;
935 for (
const auto& qualifier : kv.second) {
938 if (rt == RiskType::FX && qualifier == calcCcy) {
940 DLOG(
"Not calculating concentration risk for qualifier "
941 << qualifier <<
" of risk type " << rt
942 <<
" since the qualifier equals the SIMM calculation currency " << calcCcy);
948 auto pQualifier = crifByQualifierAndBucket[std::make_pair(qualifier,bucket)];
951 for (
auto it = pQualifier.begin(); it != pQualifier.end(); ++it) {
954 concentrationRisk[qualifier] += it->amountResultCcy * sigma * hvr;
960 concentrationRisk[qualifier] /= concThreshold;
962 concentrationRisk[qualifier] =
max(1.0,
sqrt(std::abs(concentrationRisk[qualifier])));
968 auto pBucket = crifByBucket[bucket];
969 for (
auto itOuter = pBucket.begin(); itOuter != pBucket.end(); ++itOuter) {
971 if (rt == RiskType::FX && itOuter->qualifier == calcCcy) {
973 DLOG(
"Skipping qualifier " << itOuter->qualifier <<
" of risk type " << rt
974 <<
" since the qualifier equals the SIMM calculation currency "
980 Real rwOuter =
simmConfiguration_->weight(rt, itOuter->qualifier, itOuter->label1, calcCcy);
982 Real sigmaOuter =
simmConfiguration_->sigma(rt, itOuter->qualifier, itOuter->label1, calcCcy);
985 rwOuter * (itOuter->amountResultCcy * sigmaOuter * hvr) * concentrationRisk[itOuter->qualifier];
987 Real outerConcentrationRisk = concentrationRisk.at(itOuter->qualifier);
989 sumWeightedSensis[bucket] += wsOuter;
991 bucketMargin[bucket] += wsOuter * wsOuter;
993 for (
auto itInner = pBucket.begin(); itInner != itOuter; ++itInner) {
995 if (rt == RiskType::FX && itInner->qualifier == calcCcy) {
997 DLOG(
"Skipping qualifier " << itInner->qualifier <<
" of risk type " << rt
998 <<
" since the qualifier equals the SIMM calculation currency "
1005 simmConfiguration_->correlation(rt, itOuter->qualifier, itOuter->label1, itOuter->label2, rt,
1006 itInner->qualifier, itInner->label1, itInner->label2, calcCcy);
1008 Real f =
min(outerConcentrationRisk, concentrationRisk.at(itInner->qualifier)) /
1009 max(outerConcentrationRisk, concentrationRisk.at(itInner->qualifier));
1011 Real sigmaInner =
simmConfiguration_->sigma(rt, itInner->qualifier, itInner->label1, calcCcy);
1012 Real rwInner =
simmConfiguration_->weight(rt, itInner->qualifier, itInner->label1, calcCcy);
1014 rwInner * (itInner->amountResultCcy * sigmaInner * hvr) * concentrationRisk[itInner->qualifier];
1015 bucketMargin[bucket] += 2 * corr * f * wsOuter * wsInner;
1019 bucketMargins[itOuter->qualifier] += wsOuter;
1023 bucketMargin[bucket] =
sqrt(
max(bucketMargin[bucket], 0.0));
1028 Real residualMargin = 0.0;
1029 if (bucketMargin.count(
"Residual") > 0) {
1030 residualMargin = bucketMargin.at(
"Residual");
1031 bucketMargin.erase(
"Residual");
1036 for (
auto itOuter = bucketMargin.begin(); itOuter != bucketMargin.end(); ++itOuter) {
1037 string outerBucket = itOuter->first;
1039 margin += itOuter->second * itOuter->second;
1042 Real sOuter =
max(
min(sumWeightedSensis.at(outerBucket), itOuter->second), -itOuter->second);
1043 for (
auto itInner = bucketMargin.begin(); itInner != itOuter; ++itInner) {
1044 string innerBucket = itInner->first;
1046 Real sInner =
max(
min(sumWeightedSensis.at(innerBucket), itInner->second), -itInner->second);
1050 string innerQualifier = *buckets.at(innerBucket).begin();
1051 string outerQualifier = *buckets.at(outerBucket).begin();
1052 Real corr =
simmConfiguration_->correlation(rt, outerQualifier,
"",
"", rt, innerQualifier,
"",
"", calcCcy);
1053 margin += 2.0 * sOuter * sInner * corr;
1059 margin += residualMargin;
1061 bucketMargins[
"Residual"] = residualMargin;
1065 for (
const auto& m : bucketMargin)
1066 bucketMargins[m.first] = m.second;
1068 for (
auto& m : bucketMargins)
1069 m.second = std::abs(m.second);
1071 bucketMargins[
"All"] =
margin;
1072 return make_pair(bucketMargins,
true);
1075pair<map<string, Real>,
bool>
1077 const SimmSide& side,
const Crif& crif,
bool rfLabels)
const {
1085 map<string, Real> bucketMargins;
1087 bool riskClassIsFX = rt == RiskType::FX || rt == RiskType::FXVol;
1090 Real multiplier = side == SimmSide::Call ? 1.0 : -1.0;
1093 map<string, set<string>> buckets;
1094 for(
const auto& it : crif.
filterBy(nettingSetDetails, pc, rt)) {
1095 buckets[it.bucket].insert(it.qualifier);
1099 if (buckets.empty()) {
1100 bucketMargins[
"All"] = 0.0;
1101 return make_pair(bucketMargins,
false);
1108 map<string, Real> sumWeightedSensis;
1109 map<string, map<string, Real>> sumAbsTemp;
1110 map<string, Real> sumAbsWeightedSensis;
1113 for (
const auto& kv : buckets) {
1114 string bucket = kv.first;
1115 sumAbsTemp[bucket] = {};
1119 auto pBucket = crif.
filterByBucket(nettingSetDetails, pc, rt, bucket);
1120 for (
auto itOuter = pBucket.begin(); itOuter != pBucket.end(); ++itOuter) {
1124 Real sigmaOuter =
simmConfiguration_->sigma(rt, itOuter->qualifier, itOuter->label1, calcCcy);
1128 Real wsOuter = sfOuter * ((itOuter->amountResultCcy * multiplier) * sigmaOuter);
1133 bucket ==
"12" && rt == RiskType::EquityVol) {
1137 sumWeightedSensis[bucket] += wsOuter;
1138 sumAbsTemp[bucket][itOuter->qualifier] += rfLabels ? std::abs(wsOuter) : wsOuter;
1142 for (
auto itInner = pBucket.begin(); itInner != itOuter; ++itInner) {
1144 Real corr =
simmConfiguration_->correlation(rt, itOuter->qualifier, itOuter->label1, itOuter->label2,
1145 rt, itInner->qualifier, itInner->label1, itInner->label2,
1149 Real sigmaInner =
simmConfiguration_->sigma(rt, itInner->qualifier, itInner->label1, calcCcy);
1150 Real wsInner = sfInner * ((itInner->amountResultCcy * multiplier) * sigmaInner);
1156 bucketMargins[itOuter->qualifier] += wsOuter;
1164 for (
const auto& kv : sumAbsTemp[bucket]) {
1165 sumAbsWeightedSensis[bucket] += std::abs(kv.second);
1171 Real residualMargin = 0.0;
1172 Real residualSum = 0.0;
1173 Real residualAbsSum = 0.0;
1176 residualSum = sumWeightedSensis.at(
"Residual");
1177 residualAbsSum = sumAbsWeightedSensis.at(
"Residual");
1180 sumWeightedSensis.erase(
"Residual");
1181 sumAbsWeightedSensis.erase(
"Residual");
1188 auto acc = [](
const Real p,
const pair<const string, Real>& kv) {
return p + kv.second; };
1189 Real sumSensis = accumulate(sumWeightedSensis.begin(), sumWeightedSensis.end(), 0.0, acc);
1190 Real sumAbsSensis = accumulate(sumAbsWeightedSensis.begin(), sumAbsWeightedSensis.end(), 0.0, acc);
1193 Real theta =
min(sumSensis / sumAbsSensis, 0.0);
1195 string outerBucket = itOuter->first;
1197 margin += itOuter->second * itOuter->second;
1200 Real sOuter =
max(
min(sumWeightedSensis.at(outerBucket), itOuter->second), -itOuter->second);
1201 for (
auto itInner =
curvatureMargin.begin(); itInner != itOuter; ++itInner) {
1202 string innerBucket = itInner->first;
1204 Real sInner =
max(
min(sumWeightedSensis.at(innerBucket), itInner->second), -itInner->second);
1208 string innerQualifier = *buckets.at(innerBucket).begin();
1209 string outerQualifier = *buckets.at(outerBucket).begin();
1210 Real corr =
simmConfiguration_->correlation(rt, outerQualifier,
"",
"", rt, innerQualifier,
"",
"", calcCcy);
1211 margin += 2.0 * sOuter * sInner * corr * corr;
1219 Real theta =
min(residualSum / residualAbsSum, 0.0);
1227 bucketMargins[m.first] = m.second;
1229 for (
auto& m : bucketMargins)
1230 m.second = std::abs(m.second);
1232 bucketMargins[
"All"] =
margin;
1233 return make_pair(bucketMargins,
true);
1237 const string& regulation,
const Crif& crif) {
1240 auto& results =
simmResults_[side][nettingSetDetails][regulation];
1242 const bool overwrite =
false;
1245 DLOG(
"Calculating additional margin for portfolio [" << nettingSetDetails <<
"], regulation " << regulation
1246 <<
" and SIMM side " << side);
1251 auto pc = ProductClass::Empty;
1252 auto rt = RiskType::ProductClassMultiplier;
1253 auto pIt = crif.
filterBy(nettingSetDetails, pc, rt);
1255 for(
const auto& it : pIt) {
1258 if (results.has(qpc, RiskClass::All, MarginType::All,
"All")) {
1259 Real im = results.get(qpc, RiskClass::All, MarginType::All,
"All");
1260 Real factor = it.amount;
1261 QL_REQUIRE(factor >= 0.0,
"SIMM Calculator: Amount for risk type "
1262 << rt <<
" must be greater than or equal to 0 but we got " << factor);
1263 Real pcmMargin = (factor - 1.0) * im;
1264 add(nettingSetDetails, regulation, qpc, RiskClass::All, MarginType::AdditionalIM,
"All", pcmMargin, side,
1268 add(nettingSetDetails, regulation, qpc, RiskClass::All, MarginType::All,
"All", pcmMargin, side, overwrite);
1270 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, MarginType::AdditionalIM,
"All", pcmMargin,
1273 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, MarginType::All,
"All", pcmMargin, side,
1276 if (side == SimmSide::Call)
1285 pIt = crif.
filterBy(nettingSetDetails, pc, RiskType::AddOnFixedAmount);
1286 for(
const auto& it : pIt){
1287 Real fixedMargin = it.amountResultCcy;
1288 add(nettingSetDetails, regulation, ProductClass::AddOnFixedAmount, RiskClass::All, MarginType::AdditionalIM,
1289 "All", fixedMargin, side, overwrite);
1292 add(nettingSetDetails, regulation, ProductClass::AddOnFixedAmount, RiskClass::All, MarginType::All,
"All",
1296 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, MarginType::AdditionalIM,
"All",
1297 fixedMargin, side, overwrite);
1299 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, MarginType::All,
"All", fixedMargin, side,
1302 if (side == SimmSide::Call)
1311 pIt = crif.
filterBy(nettingSetDetails, pc, RiskType::AddOnNotionalFactor);
1312 for(
const auto& it : pIt){
1315 auto pQualifierIt = crif.
filterByQualifier(nettingSetDetails, pc, RiskType::Notional, it.qualifier);
1316 const auto count = pQualifierIt.size();
1317 QL_REQUIRE(
count < 2,
"Expected either 0 or 1 elements for risk type "
1318 << RiskType::Notional <<
" and qualifier " << it.qualifier
1319 <<
" but got " <<
count);
1323 Real notional = pQualifierIt.front().amountResultCcy;
1324 Real factor = it.amount;
1325 Real notionalFactorMargin = notional * factor / 100.0;
1327 add(nettingSetDetails, regulation, ProductClass::AddOnNotionalFactor, RiskClass::All,
1328 MarginType::AdditionalIM,
"All", notionalFactorMargin, side, overwrite);
1331 add(nettingSetDetails, regulation, ProductClass::AddOnNotionalFactor, RiskClass::All, MarginType::All,
1333 notionalFactorMargin, side, overwrite);
1335 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, MarginType::AdditionalIM,
"All",
1336 notionalFactorMargin, side, overwrite);
1338 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, MarginType::All,
"All",
1339 notionalFactorMargin,
1342 if (side == SimmSide::Call)
1352 const string& regulation) {
1355 LOG(
"SimmCalculator: Populating higher level results")
1366 auto& results =
simmResults_[side][nettingSetDetails][regulation];
1369 for (
const auto& pc : pcs) {
1370 for (
const auto& rc : rcs) {
1373 Real riskClassMargin = 0.0;
1374 bool hasRiskClass =
false;
1375 for (
const auto& mt : mts) {
1376 if (results.has(pc, rc, mt,
"All")) {
1377 riskClassMargin += results.get(pc, rc, mt,
"All");
1379 hasRiskClass =
true;
1385 add(nettingSetDetails, regulation, pc, rc, MarginType::All,
"All", riskClassMargin, side);
1391 for (
const auto& pc : pcs) {
1392 Real productClassMargin = 0.0;
1393 bool hasProductClass =
false;
1397 for (
auto ito = rcs.begin(); ito != rcs.end(); ++ito) {
1399 if (!results.has(pc, *ito, MarginType::All,
"All"))
1402 if (!hasProductClass)
1403 hasProductClass =
true;
1405 Real imo = results.get(pc, *ito, MarginType::All,
"All");
1407 productClassMargin += imo * imo;
1410 for (
auto iti = rcs.begin(); iti != ito; ++iti) {
1411 if (!results.has(pc, *iti, MarginType::All,
"All"))
1413 Real imi = results.get(pc, *iti, MarginType::All,
"All");
1416 productClassMargin += 2.0 * corr * imo * imi;
1421 if (hasProductClass) {
1422 productClassMargin =
sqrt(
max(productClassMargin, 0.0));
1423 add(nettingSetDetails, regulation, pc, RiskClass::All, MarginType::All,
"All", productClassMargin, side);
1430 for (
const auto& pc : pcs) {
1431 if (results.has(pc, RiskClass::All, MarginType::All,
"All")) {
1432 im += results.get(pc, RiskClass::All, MarginType::All,
"All");
1435 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, MarginType::All,
"All", im, side);
1440 for (
const auto& pc : pcs) {
1441 for (
const auto& mt : mts) {
1443 bool hasPcMt =
false;
1447 for (
auto ito = rcs.begin(); ito != rcs.end(); ++ito) {
1449 if (!results.has(pc, *ito, mt,
"All"))
1455 Real imo = results.get(pc, *ito, mt,
"All");
1460 for (
auto iti = rcs.begin(); iti != ito; ++iti) {
1461 if (!results.has(pc, *iti, mt,
"All"))
1463 Real imi = results.get(pc, *iti, mt,
"All");
1466 margin += 2.0 * corr * imo * imi;
1473 add(nettingSetDetails, regulation, pc, RiskClass::All, mt,
"All",
margin, side);
1479 for (
const auto& rc : rcs) {
1480 for (
const auto& mt : mts) {
1482 bool hasRcMt =
false;
1485 for (
const auto& pc : pcs) {
1487 if (!results.has(pc, rc, mt,
"All"))
1493 margin += results.get(pc, rc, mt,
"All");
1498 add(nettingSetDetails, regulation, ProductClass::All, rc, mt,
"All",
margin, side);
1506 for (
const auto& rc : rcs) {
1510 for (
const auto& pc : pcs) {
1512 if (!results.has(pc, rc, MarginType::All,
"All"))
1518 margin += results.get(pc, rc, MarginType::All,
"All");
1523 add(nettingSetDetails, regulation, ProductClass::All, rc, MarginType::All,
"All",
margin, side);
1530 for (
const auto& mt : mts) {
1534 for (
const auto& pc : pcs) {
1536 if (!results.has(pc, RiskClass::All, mt,
"All"))
1542 margin += results.get(pc, RiskClass::All, mt,
"All");
1547 add(nettingSetDetails, regulation, ProductClass::All, RiskClass::All, mt,
"All",
margin, side);
1555 LOG(
"SimmCalculator: Populating final winning regulators' IM");
1561 tids.second.clear();
1562 for (
const auto& sv : winningRegs) {
1566 for (
const auto& nv : sv.second) {
1568 string winningReg = nv.second;
1572 if (
tradeIds_.at(side).at(nsd).count(winningReg) > 0)
1573 for (
const string& tid :
tradeIds_.at(side).at(nsd).at(winningReg))
1582 for (
const auto& nv : sv.second) {
1587 const SimmResults
simmResults = nv.second.find(reg) == nv.second.end()
1589 : nv.second.at(reg);
1601 const bool overwrite) {
1603 DLOG(
"Calculated " << side <<
" margin for [netting set details, product class, risk class, margin type] = ["
1605 <<
", " << pc <<
", " << rc <<
", " << mt <<
"] of " <<
margin);
1614 const bool overwrite) {
1616 for (
const auto& kv : margins)
1617 add(nettingSetDetails, regulation, pc, rc, mt, kv.first, kv.second, side, overwrite);
1621 for (
const auto& crifRecord : crif) {
1622 for (
const auto& side : {SimmSide::Call, SimmSide::Post}) {
1625 bool collectRegsIsEmpty =
false;
1626 bool postRegsIsEmpty =
false;
1633 if (enforceIMRegulations)
1634 regsString = side == SimmSide::Call ? crifRecord.collectRegulations : crifRecord.postRegulations;
1637 auto newCrifRecord = crifRecord;
1638 newCrifRecord.collectRegulations.clear();
1639 newCrifRecord.postRegulations.clear();
1640 for (
const string& r : regs) {
1641 if (r ==
"Excluded" ||
1642 (r ==
"Unspecified" && enforceIMRegulations && !(collectRegsIsEmpty && postRegsIsEmpty))) {
1644 }
else if (r !=
"Excluded") {
1646 if (!newCrifRecord.isSimmParameter())
1647 tradeIds_[side][nettingSetDetails][r].insert(newCrifRecord.tradeId);
1651 const bool onDiffAmountCcy =
true;
1652 regSensitivities_[side][nettingSetDetails][r].addRecord(newCrifRecord, onDiffAmountCcy);
1662 static Real q = boost::math::quantile(boost::math::normal(), 0.995);
1664 return (q * q - 1.0) * (1.0 + theta) - theta;
1670 const std::vector<CrifRecord::RiskType>& riskTypes)
const {
1671 std::set<std::string> qualifiers;
1672 for (
auto& rt : riskTypes) {
1673 auto qualis = crif.
qualifiersBy(nettingSetDetails, pc, rt);
1674 qualifiers.insert(qualis.begin(), qualis.end());
Crif aggregate() const
Aggregate all existing records.
std::set< std::string > qualifiersBy(const NettingSetDetails nsd, CrifRecord::ProductClass pc, const CrifRecord::RiskType rt) const
std::vector< CrifRecord > filterByQualifier(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &qualifier) const
std::vector< CrifRecord > filterByBucket(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &bucket) const
std::set< CrifRecord::ProductClass > ProductClassesByNettingSetDetails(const NettingSetDetails nsd) const
std::set< CrifRecord >::const_iterator end() const
void addRecord(const CrifRecord &record, bool aggregateDifferentAmountCurrencies=false, bool sortFxVolQualifer=true)
size_t countMatching(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &qualifier) const
std::set< CrifRecord >::const_iterator findBy(const NettingSetDetails nsd, CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &qualifier) const
Find first element.
std::vector< CrifRecord > filterBy(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt) const
const std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::map< std::string, SimmResults > > > & simmResults() const
void populateFinalResults()
std::pair< std::map< std::string, QuantLib::Real >, bool > margin(const ore::data::NettingSetDetails &nettingSetDetails, const CrifRecord::ProductClass &pc, const CrifRecord::RiskType &rt, const ore::analytics::Crif &netRecords, const SimmSide &side) const
std::set< std::string > getQualifiers(const Crif &crif, const ore::data::NettingSetDetails &nettingSetDetails, const CrifRecord::ProductClass &pc, const std::vector< CrifRecord::RiskType > &riskTypes) const
std::map< SimmSide, std::set< NettingSetDetails > > hasCFTC_
std::map< SimmSide, std::set< NettingSetDetails > > hasSEC_
std::pair< std::map< std::string, QuantLib::Real >, bool > irDeltaMargin(const ore::data::NettingSetDetails &nettingSetDetails, const CrifRecord::ProductClass &pc, const ore::analytics::Crif &netRecords, const SimmSide &side) const
Calculate the Interest Rate delta margin component for the given portfolio and product class.
std::string calculationCcyCall_
The SIMM exposure calculation currency i.e. the currency for which FX delta risk is ignored.
QuantLib::Real lambda(QuantLib::Real theta) const
Give the used in the curvature margin calculation.
std::pair< std::map< std::string, QuantLib::Real >, bool > irVegaMargin(const ore::data::NettingSetDetails &nettingSetDetails, const CrifRecord::ProductClass &pc, const ore::analytics::Crif &netRecords, const SimmSide &side) const
Calculate the Interest Rate vega margin component for the given portfolio and product class.
void calcAddMargin(const SimmSide &side, const ore::data::NettingSetDetails &nsd, const string ®ulation, const ore::analytics::Crif &netRecords)
Calculate the additional initial margin for the portfolio ID and regulation.
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::string > > winningRegulations_
Regulation with highest initial margin for each given netting set.
std::pair< std::map< std::string, QuantLib::Real >, bool > irCurvatureMargin(const ore::data::NettingSetDetails &nettingSetDetails, const CrifRecord::ProductClass &pc, const SimmSide &side, const ore::analytics::Crif &crif) const
Calculate the Interest Rate curvature margin component for the given portfolio and product class.
std::map< ore::data::NettingSetDetails, bool > collectRegsIsEmpty_
For each netting set, whether all CRIF records' collect regulations are empty.
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::map< std::string, SimmResults > > > simmResults_
std::map< ore::data::NettingSetDetails, bool > postRegsIsEmpty_
For each netting set, whether all CRIF records' post regulations are empty.
SimmCalculator(const ore::analytics::Crif &crif, const QuantLib::ext::shared_ptr< SimmConfiguration > &simmConfiguration, const std::string &calculationCcyCall="USD", const std::string &calculationCcyPost="USD", const std::string &resultCcy="", const QuantLib::ext::shared_ptr< ore::data::Market > market=nullptr, const bool determineWinningRegulations=true, const bool enforceIMRegulations=false, const bool quiet=false, const std::map< SimmSide, std::set< NettingSetDetails > > &hasSEC=std::map< SimmSide, std::set< NettingSetDetails > >(), const std::map< SimmSide, std::set< NettingSetDetails > > &hasCFTC=std::map< SimmSide, std::set< NettingSetDetails > >())
const std::map< SimmSide, std::map< ore::data::NettingSetDetails, string > > & winningRegulations() const
QuantLib::ext::shared_ptr< SimmConfiguration > simmConfiguration_
The SIMM configuration governing the calculation.
ore::analytics::Crif simmParameters_
Record of SIMM parameters that were used in the calculation.
std::pair< std::map< std::string, QuantLib::Real >, bool > curvatureMargin(const ore::data::NettingSetDetails &nettingSetDetails, const CrifRecord::ProductClass &pc, const CrifRecord::RiskType &rt, const SimmSide &side, const ore::analytics::Crif &netRecords, bool rfLabels=true) const
const std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::pair< std::string, SimmResults > > > & finalSimmResults() const
QuantLib::ext::shared_ptr< ore::data::Market > market_
Market data for FX rates to use for converting amounts to USD.
bool quiet_
If true, no logging is written out.
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::pair< std::string, SimmResults > > > finalSimmResults_
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::map< std::string, Crif > > > regSensitivities_
Net sentivities at the regulation level within each netting set.
void add(const ore::data::NettingSetDetails &nettingSetDetails, const string ®ulation, const CrifRecord::ProductClass &pc, const SimmConfiguration::RiskClass &rc, const SimmConfiguration::MarginType &mt, const std::string &b, QuantLib::Real margin, SimmSide side, const bool overwrite=true)
std::string calculationCcyPost_
ore::analytics::Crif crif_
All the net sensitivities passed in for the calculation.
void populateResults(const SimmSide &side, const ore::data::NettingSetDetails &nsd, const string ®ulation)
std::map< SimmSide, set< string > > finalTradeIds_
void splitCrifByRegulationsAndPortfolios(const Crif &crif, const bool enforceIMRegulations)
Add CRIF record to the CRIF records container that correspondsd to the given regulation/s and portfol...
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::map< std::string, set< string > > > > tradeIds_
Container for keeping track of what trade IDs belong to each regulation.
std::string resultCcy_
The SIMM result currency i.e. the currency in which the main SIMM results are denominated.
const void calculateRegulationSimm(const ore::analytics::Crif &crif, const ore::data::NettingSetDetails &nsd, const string ®ulation, const SimmSide &side)
Calculates SIMM for a given regulation under a given netting set.
SimmConfiguration::SimmSide SimmSide
SimmSide
Enum indicating the relevant side of the SIMM calculation.
Regulation
SIMM regulators.
Class for loading CRIF records.
Struct for holding a CRIF record.
bool parseBool(const string &s)
bool checkCurrency(const string &code)
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable sqrt(RandomVariable x)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
RandomVariable min(RandomVariable x, const RandomVariable &y)
SimmConfiguration::RiskClass RiskClass
SimmConfiguration::Regulation getWinningRegulation(const std::vector< string > &winningRegulations)
From a vector of regulations, determine the winning regulation based on order of priority.
set< string > parseRegulationString(const string ®sString, const set< string > &valueIfEmpty)
Reads a string containing regulations applicable for a given CRIF record.
SimmConfiguration::MarginType MarginType
CrifRecord::RiskType RiskType
CrifRecord::ProductClass ProductClass
SimmVersion
Ordered SIMM versions.
SimmVersion parseSimmVersion(const string &version)
SimmConfiguration::Regulation Regulation
CrifRecord::ProductClass parseProductClass(const string &pc)
SimmConfiguration::SimmSide SimmSide
std::string to_string(const LocationInfo &l)
Class for calculating SIMM.
Base SIMM configuration class.
std::string amountCurrency
std::string resultCurrency
std::string collectRegulations
QuantLib::Real amountResultCcy
std::string postRegulations