22#include <boost/math/distributions/normal.hpp>
23#include <boost/optional/optional_io.hpp>
26#include <ql/errors.hpp>
27#include <ql/math/comparison.hpp>
28#include <ql/math/matrix.hpp>
29#include <ql/math/matrixutilities/symmetricschurdecomposition.hpp>
35using QuantLib::Integer;
53vector<string> lookup(
const RiskType& rt,
const map<
RiskType, vector<string>>& m) {
54 if (m.count(rt) > 0) {
61std::ostream&
operator<<(std::ostream& out,
const tuple<string, string, string>& tup) {
62 return out <<
"[Bucket: '" << std::get<0>(tup) <<
"', Label1: '" << std::get<1>(tup) <<
"', Label2: '"
63 << std::get<2>(tup) <<
"']";
71 const string& label2)
const {
76 const std::string& name,
const std::string version, Size mporDays)
77 : name_(
name), version_(version), simmBucketMapper_(simmBucketMapper), mporDays_(mporDays) {}
82 QL_REQUIRE(
hasBuckets(rt),
"The SIMM risk type " << rt <<
" does not have buckets");
87 return std::find(container.begin(), container.end(),
value) != container.end();
92 "The risk type " << rt <<
" is not valid for SIMM configuration with name" <<
name_);
98 "The risk type " << rt <<
" is not valid for SIMM configuration with name" <<
name_);
104 "The risk type " << rt <<
" is not valid for SIMM configuration with name" <<
name_);
109 boost::optional<std::string> label_1,
const std::string&)
const {
112 "The risk type " << rt <<
" is not valid for SIMM configuration with name" <<
name_);
120 QL_REQUIRE(qualifier,
"Need a valid qualifier to return a risk weight because the risk type "
121 << rt <<
" has bucket dependent risk weights");
122 QL_REQUIRE(!
buckets(rt).empty(),
"Could not find any buckets for risk type " << rt);
131 QL_FAIL(
"Could not find risk weight for risk type " << rt <<
" and key " << bucketKey);
137 QL_REQUIRE(label_1,
"Need a valid Label1 value to return a risk weight because the risk type "
138 << rt <<
" has bucket and Label1 dependent risk weights");
139 QL_REQUIRE(!
labels1(rt).empty(),
"Could not find any Label1 values for risk type " << rt);
145 QL_FAIL(
"Could not find risk weight for risk type " << rt <<
" and key " << label1Key);
149 QL_FAIL(
"Could not find a risk weight for (risk type, qualifier, Label1) = (" << rt <<
"," << qualifier <<
","
156 "The risk type " << rt <<
" is not valid for SIMM configuration with name" <<
name_);
158 QL_REQUIRE(
curvatureWeights_.count(rt) > 0,
"The risk type " << rt <<
" does not have a curvature weight.");
160 QL_REQUIRE(!
labels1(rt).empty(),
"Could not find any Label1 values for risk type " << rt);
168 "The risk type " << rt <<
" is not valid for SIMM configuration with name" <<
name_);
178 boost::optional<std::string> label_1,
const std::string& calculationCurrency)
const {
183 Real deltaRiskWeight;
184 std::string ccy1, ccy2;
186 case RiskType::CommodityVol:
187 deltaRiskWeight =
weight(RiskType::Commodity, qualifier, label_1);
190 case RiskType::EquityVol:
191 deltaRiskWeight =
weight(RiskType::Equity, qualifier, label_1);
194 case RiskType::FXVol:
201 ccy1 = (*qualifier).substr(0, 3);
202 ccy2 = (*qualifier).substr(3, 3);
206 deltaRiskWeight =
weight(RiskType::FX, ccy1, label_1, ccy2);
217 const string& firstLabel_1,
const string& firstLabel_2,
218 const RiskType& secondRt,
const string& secondQualifier,
219 const string& secondLabel_1,
const string& secondLabel_2,
220 const std::string&)
const {
224 "The risk type " << firstRt <<
" is not valid for SIMM configuration with name" <<
name());
226 "The risk type " << secondRt <<
" is not valid for SIMM configuration with name" <<
name());
229 if (firstRt == secondRt && firstQualifier == secondQualifier && firstLabel_1 == secondLabel_1 &&
230 firstLabel_2 == secondLabel_2) {
235 if ((firstRt == RiskType::Equity && secondRt == RiskType::Equity) ||
236 (firstRt == RiskType::EquityVol && secondRt == RiskType::EquityVol)) {
243 if (bucket_1 ==
"Residual" || bucket_2 ==
"Residual") {
244 return firstQualifier == secondQualifier ? 1.0 : 0.0;
249 if (bucket_1 == bucket_2) {
250 auto bucketKey =
makeKey(bucket_1,
"",
"");
255 auto label12Key =
makeKey(
"", bucket_1, bucket_2);
261 if ((firstRt == RiskType::CreditQ && secondRt == RiskType::CreditQ) ||
262 (firstRt == RiskType::CreditVol && secondRt == RiskType::CreditVol)) {
269 if (bucket_1 ==
"Residual" || bucket_2 ==
"Residual") {
270 if (bucket_1 == bucket_2) {
280 if (bucket_1 == bucket_2) {
282 if (firstQualifier != secondQualifier) {
292 auto label12Key =
makeKey(
"", bucket_1, bucket_2);
296 QL_FAIL(
"Could not find correlation for risk type " << rt <<
" and key " << label12Key);
301 if ((firstRt == RiskType::CreditNonQ && secondRt == RiskType::CreditNonQ) ||
302 (firstRt == RiskType::CreditVolNonQ && secondRt == RiskType::CreditVolNonQ)) {
309 if (bucket_1 ==
"Residual" || bucket_2 ==
"Residual") {
310 if (bucket_1 == bucket_2) {
320 if (bucket_1 == bucket_2) {
341 if ((firstRt == RiskType::Commodity && secondRt == RiskType::Commodity) ||
342 (firstRt == RiskType::CommodityVol && secondRt == RiskType::CommodityVol)) {
348 if (bucket_1 == bucket_2) {
349 auto bucketKey =
makeKey(bucket_1,
"",
"");
351 return firstQualifier == secondQualifier ? 1.0 :
intraBucketCorrelation_.at(RiskType::Commodity).at(bucketKey);
354 auto label12Key =
makeKey(
"", bucket_1, bucket_2);
360 if ((firstRt != secondRt) && (firstQualifier == secondQualifier)) {
361 if (((firstRt == RiskType::IRCurve || firstRt == RiskType::Inflation) && secondRt == RiskType::XCcyBasis) ||
362 (firstRt == RiskType::XCcyBasis && (secondRt == RiskType::IRCurve || secondRt == RiskType::Inflation))) {
366 if ((firstRt == RiskType::IRCurve && secondRt == RiskType::Inflation) ||
367 (firstRt == RiskType::Inflation && secondRt == RiskType::IRCurve)) {
371 if ((firstRt == RiskType::IRVol && secondRt == RiskType::InflationVol) ||
372 (firstRt == RiskType::InflationVol && secondRt == RiskType::IRVol)) {
379 if ((firstRt == RiskType::IRCurve && secondRt == RiskType::IRCurve) ||
380 (firstRt == RiskType::IRVol && secondRt == RiskType::IRVol)) {
383 if (firstQualifier == secondQualifier) {
385 if (firstLabel_2 != secondLabel_2) {
387 firstLabel_1 ==
"" && secondLabel_1 ==
"",
388 "When asking for Label2 level correlations, "
389 <<
"the Label1 level values should both contain the default parameter i.e. empty string");
390 QL_REQUIRE(firstRt != RiskType::IRVol,
"There is no correlation at the Label2 level for Risk_IRVol");
396 auto label12Key =
makeKey(
"", firstLabel_1, secondLabel_1);
400 QL_FAIL(
"Could not find correlation for risk type " << rt <<
" and key " << label12Key);
408 if (firstRt == RiskType::InflationVol && secondRt == RiskType::InflationVol) {
417 if ((firstRt == RiskType::FX && secondRt == RiskType::FX) ||
418 (firstRt == RiskType::FXVol && secondRt == RiskType::FXVol)) {
419 return firstQualifier == secondQualifier ? 1.0 :
fxCorr_;
423 if (firstRt == RiskType::BaseCorr && secondRt == RiskType::BaseCorr) {
439 return sqrt(365.0 / (1.4 *
mporDays_)) / boost::math::quantile(boost::math::normal(), 0.99);
450 "Could not find risk class correlation between " << rc_1 <<
" and " << rc_2 <<
".");
456 QL_REQUIRE(!labels.empty(),
"Labels cannot be empty");
457 auto it = std::find(labels.begin(), labels.end(), label);
458 QL_REQUIRE(it != labels.end(),
"The label '" << label <<
"' could not be found in the labels.");
459 return std::distance(labels.begin(), it);
464 QL_REQUIRE(rt == RiskType::CreditQ,
"addLabels2 only supported for RiskType_CreditQ");
467 if (label_2.size() == 3) {
468 QL_REQUIRE(
checkCurrency(label_2),
"Expected a Label2 of size 3 to be a valid currency code");
471 if (label_2.size() == 7) {
472 QL_REQUIRE(
checkCurrency(label_2.substr(0, 3)),
"Expected first 3 characters of Label2 ("
473 << label_2.substr(0, 3) <<
") to be a valid currency code");
474 QL_REQUIRE(label_2.substr(4) ==
"Sec",
"Last 3 characters of Label2 should be 'Sec'");
475 QL_REQUIRE(
checkCurrency(label_2.substr(3, 1)),
"Delimiter should be a comma");
478 QL_FAIL(
"Label2 passed to addLabels2 is unusable for RiskType " << rt);
virtual std::string label2(const QuantLib::ext::shared_ptr< QuantLib::InterestRateIndex > &irIndex) const
QuantLib::Real correlation(const CrifRecord::RiskType &firstRt, const std::string &firstQualifier, const std::string &firstLabel_1, const std::string &firstLabel_2, const CrifRecord::RiskType &secondRt, const std::string &secondQualifier, const std::string &secondLabel_1, const std::string &secondLabel_2, const std::string &calculationCurrency="") const override
const std::tuple< std::string, std::string, std::string > makeKey(const std::string &, const std::string &, const std::string &) const
QuantLib::Real crqResidualIntraCorr_
Credit-Q residual intra correlation.
SimmConfigurationBase(const QuantLib::ext::shared_ptr< SimmBucketMapper > &simmBucketMapper, const std::string &name, const std::string version, QuantLib::Size mporDays=10)
Constructor taking the SIMM configuration name and version.
QuantLib::Real basecorrCorr_
Base correlation risk factor correlation.
std::vector< std::string > buckets(const CrifRecord::RiskType &rt) const override
QuantLib::Real correlationRiskClasses(const RiskClass &rc_1, const RiskClass &rc_2) const override
Return the correlation between SIMM risk classes rc_1 and rc_2.
std::map< CrifRecord::RiskType, QuantLib::Real > rwRiskType_
QuantLib::Real weight(const CrifRecord::RiskType &rt, boost::optional< std::string > qualifier=boost::none, boost::optional< std::string > label_1=boost::none, const std::string &calculationCurrency="") const override
QuantLib::Size labelIndex(const std::string &label, const std::vector< std::string > &labels) const
Helper method to find the index of the label in labels.
std::string name_
Name of the SIMM configuration.
QuantLib::Real crnqResidualIntraCorr_
Credit-NonQ residual intra correlation.
std::string version_
SIMM configuration version.
bool hasBuckets(const CrifRecord::RiskType &rt) const override
Return true if the SIMM risk type rt has buckets.
std::map< CrifRecord::RiskType, std::vector< std::string > > mapLabels_2_
bool isValidRiskType(const CrifRecord::RiskType &rt) const override
QuantLib::Real irInterCurrencyCorr_
IR correlation across currencies.
std::vector< std::string > labels2(const CrifRecord::RiskType &rt) const override
std::map< CrifRecord::RiskType, Amounts > rwLabel_1_
std::map< CrifRecord::RiskType, Amounts > intraBucketCorrelation_
std::map< CrifRecord::RiskType, std::vector< std::string > > mapBuckets_
QuantLib::Real crnqDiffIntraCorr_
Credit-NonQ non-residual intra correlation when different underlying names.
QuantLib::Real sigma(const CrifRecord::RiskType &rt, boost::optional< std::string > qualifier=boost::none, boost::optional< std::string > label_1=boost::none, const std::string &calculationCurrency="") const override
QuantLib::Real crqSameIntraCorr_
Credit-Q non-residual intra correlation when same qualifier but different vertex/source.
const std::string & name() const override
Returns the SIMM configuration name.
QuantLib::Real crnqSameIntraCorr_
Credit-NonQ non-residual intra correlation when same underlying names.
std::vector< std::string > labels1(const CrifRecord::RiskType &rt) const override
const bool checkValue(const std::string &, const std::vector< std::string > &) const
QuantLib::Real sigmaMultiplier() const
Calculate variable for use in sigma method.
QuantLib::Real curvatureWeight(const CrifRecord::RiskType &rt, const std::string &label_1) const override
QuantLib::Real fxCorr_
FX correlation.
QuantLib::Real infCorr_
Correlation between any yield and inflation in same currency.
Amounts riskClassCorrelation_
Risk class correlation matrix.
QuantLib::Real crnqInterCorr_
Credit-NonQ non-residual inter bucket correlation.
std::map< CrifRecord::RiskType, QuantLib::Real > historicalVolatilityRatios_
Map from risk type to a historical volatility ratio.
std::map< CrifRecord::RiskType, Amounts > interBucketCorrelation_
std::map< CrifRecord::RiskType, std::vector< std::string > > mapLabels_1_
QuantLib::Real infVolCorr_
Correlation between any yield volatility and inflation volatility in same currency.
QuantLib::Real crqDiffIntraCorr_
Credit-Q non-residual intra correlation when different qualifier.
QuantLib::Real irSubCurveCorr_
IR Label2 level i.e. sub-curve correlation.
QuantLib::Real historicalVolatilityRatio(const CrifRecord::RiskType &rt) const override
void addLabels2Impl(const CrifRecord::RiskType &rt, const std::string &label_2)
A base implementation of addLabels2 that can be shared by derived classes.
std::map< CrifRecord::RiskType, std::vector< QuantLib::Real > > curvatureWeights_
QuantLib::ext::shared_ptr< SimmBucketMapper > simmBucketMapper_
Used to map SIMM Qualifier names to SIMM bucket values.
std::string bucket(const CrifRecord::RiskType &rt, const std::string &qualifier) const override
std::map< CrifRecord::RiskType, Amounts > rwBucket_
virtual bool isSimmConfigCalibration() const
SafeStack< ValueType > value
Currency parseCurrency(const string &s)
bool checkCurrency(const string &code)
Integer parseInteger(const string &s)
RandomVariable sqrt(RandomVariable x)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
SimmConfiguration::RiskClass RiskClass
CrifRecord::RiskType RiskType
SimmVersion
Ordered SIMM versions.
SimmVersion parseSimmVersion(const string &version)
std::string to_string(const LocationInfo &l)
Base SIMM configuration class.