Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
simmconfiguration.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 Quaternion Risk Management Ltd.
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
21
22#include <ql/math/comparison.hpp>
23#include <ql/math/matrixutilities/symmetricschurdecomposition.hpp>
24
25#include <boost/algorithm/string.hpp>
26#include <boost/assign.hpp>
27#include <boost/bimap.hpp>
28
29#include <map>
30
31namespace ore {
32namespace analytics {
33
34using boost::assign::list_of;
35using std::map;
36using std::ostream;
37using std::set;
38using std::pair;
39using std::string;
40using std::vector;
41
42using namespace QuantLib;
44
45// Following bimaps ease the conversion from enum to string and back
46// It also puts in one place a mapping from string to enum value
47
48// custom comparator for bimaps
49struct string_cmp {
50 bool operator()(const string& lhs, const string& rhs) const {
51 return ((boost::to_lower_copy(lhs)) < (boost::to_lower_copy(rhs)));
52 }
53};
54
55// Ease the notation below
56template <typename T> using bm = boost::bimap<T, boost::bimaps::set_of<string, string_cmp>>;
57
58// Initialise the bimaps
59const bm<SimmConfiguration::RiskClass> riskClassMap =
60 list_of<bm<SimmConfiguration::RiskClass>::value_type>(SimmConfiguration::RiskClass::InterestRate, "InterestRate")(
61 SimmConfiguration::RiskClass::CreditQualifying,
62 "CreditQualifying")(SimmConfiguration::RiskClass::CreditNonQualifying, "CreditNonQualifying")(
63 SimmConfiguration::RiskClass::Equity, "Equity")(SimmConfiguration::RiskClass::Commodity, "Commodity")(
64 SimmConfiguration::RiskClass::FX, "FX")(SimmConfiguration::RiskClass::All, "All");
65
67 list_of<bm<SimmConfiguration::MarginType>::value_type>(SimmConfiguration::MarginType::Delta, "Delta")(
68 SimmConfiguration::MarginType::Vega, "Vega")(SimmConfiguration::MarginType::Curvature, "Curvature")(
69 SimmConfiguration::MarginType::BaseCorr, "BaseCorr")(SimmConfiguration::MarginType::AdditionalIM, "AdditionalIM")(
70 SimmConfiguration::MarginType::All, "All");
71
73 list_of<bm<SimmConfiguration::IMModel>::value_type>(SimmConfiguration::IMModel::Schedule, "Schedule")(
76
77const bm<SimmConfiguration::Regulation> regulationsMap = list_of<bm<SimmConfiguration::Regulation>::value_type>(SimmConfiguration::Regulation::APRA, "APRA")
78 (SimmConfiguration::Regulation::CFTC, "CFTC")(SimmConfiguration::Regulation::ESA, "ESA")(SimmConfiguration::Regulation::FINMA, "FINMA")
79 (SimmConfiguration::Regulation::KFSC, "KFSC")(SimmConfiguration::Regulation::HKMA, "HKMA")(SimmConfiguration::Regulation::JFSA, "JFSA")
80 (SimmConfiguration::Regulation::MAS, "MAS")(SimmConfiguration::Regulation::OSFI, "OSFI")(SimmConfiguration::Regulation::RBI, "RBI")
81 (SimmConfiguration::Regulation::SEC, "SEC")(SimmConfiguration::Regulation::SEC_unseg, "SEC-unseg")(SimmConfiguration::Regulation::USPR, "USPR")
82 (SimmConfiguration::Regulation::NONREG, "NONREG")(SimmConfiguration::Regulation::BACEN, "BACEN")(SimmConfiguration::Regulation::SANT, "SANT")
83 (SimmConfiguration::Regulation::SFC, "SFC")(SimmConfiguration::Regulation::UK, "UK")(SimmConfiguration::Regulation::AMFQ, "AMFQ")
84 (SimmConfiguration::Regulation::Included, "Included")(SimmConfiguration::Regulation::Unspecified, "Unspecified")(SimmConfiguration::Regulation::Invalid, "Invalid");
85
86// Initialise the counts
90
91set<SimmConfiguration::RiskClass> SimmConfiguration::riskClasses(bool includeAll) {
92
93 // This only works if 'All' is the last enum value
94 Size bound = includeAll ? numberOfRiskClasses : numberOfRiskClasses - 1;
95
96 // Return the set of values
97 set<RiskClass> result;
98 for (Size i = 0; i < bound; ++i) {
99 result.insert(RiskClass(i));
100 }
101 return result;
102}
103
104set<CrifRecord::RiskType> SimmConfiguration::riskTypes(bool includeAll) {
105
106 static std::set<CrifRecord::RiskType> simmRiskTypes = {
107 // SIMM Risk Types
108 CrifRecord::RiskType::Commodity,
109 CrifRecord::RiskType::CommodityVol,
110 CrifRecord::RiskType::CreditNonQ,
111 CrifRecord::RiskType::CreditQ,
112 CrifRecord::RiskType::CreditVol,
113 CrifRecord::RiskType::CreditVolNonQ,
114 CrifRecord::RiskType::Equity,
115 CrifRecord::RiskType::EquityVol,
116 CrifRecord::RiskType::FX,
117 CrifRecord::RiskType::FXVol,
118 CrifRecord::RiskType::Inflation,
119 CrifRecord::RiskType::IRCurve,
120 CrifRecord::RiskType::IRVol,
121 CrifRecord::RiskType::InflationVol,
122 CrifRecord::RiskType::BaseCorr,
123 CrifRecord::RiskType::XCcyBasis,
124 CrifRecord::RiskType::ProductClassMultiplier,
125 CrifRecord::RiskType::AddOnNotionalFactor,
126 CrifRecord::RiskType::Notional,
127 CrifRecord::RiskType::AddOnFixedAmount,
128 CrifRecord::RiskType::PV, // IM Schedule
129 };
130
131 // This only works if 'All' is the last enum value
132 if (includeAll) {
133 simmRiskTypes.insert(CrifRecord::RiskType::All);
134 }
135 return simmRiskTypes;
136}
137
138set<SimmConfiguration::MarginType> SimmConfiguration::marginTypes(bool includeAll) {
139
140 // This only works if 'All' is the last enum value
141 Size bound = includeAll ? numberOfMarginTypes : numberOfMarginTypes - 1;
142
143 // Return the set of values
144 set<MarginType> result;
145 for (Size i = 0; i < bound; ++i) {
146 result.insert(MarginType(i));
147 }
148 return result;
149}
150
151set<CrifRecord::ProductClass> SimmConfiguration::productClasses(bool includeAll) {
152
153
154 static std::set<CrifRecord::ProductClass> simmProductClasses = {
155 CrifRecord::ProductClass::RatesFX,
156 CrifRecord::ProductClass::Rates, // extension for IM Schedule
157 CrifRecord::ProductClass::FX, // extension for IM Schedule
158 CrifRecord::ProductClass::Credit,
159 CrifRecord::ProductClass::Equity,
160 CrifRecord::ProductClass::Commodity,
161 CrifRecord::ProductClass::Empty,
162 CrifRecord::ProductClass::Other, // extension for IM Schedule
163 CrifRecord::ProductClass::AddOnNotionalFactor, // extension for additional IM
164 CrifRecord::ProductClass::AddOnFixedAmount};
165
166 if (includeAll) {
167 simmProductClasses.insert(CrifRecord::ProductClass::All);
168 }
169 return simmProductClasses;
170}
171
172pair<CrifRecord::RiskType, CrifRecord::RiskType>
174 CrifRecord::RiskType deltaRiskType, vegaRiskType;
175 switch (rc) {
176 case RiskClass::InterestRate:
177 deltaRiskType = CrifRecord::RiskType::IRCurve;
178 vegaRiskType = CrifRecord::RiskType::IRVol;
179 break;
180 case RiskClass::CreditQualifying:
181 deltaRiskType = CrifRecord::RiskType::CreditQ;
182 vegaRiskType = CrifRecord::RiskType::CreditVol;
183 break;
184 case RiskClass::CreditNonQualifying:
185 deltaRiskType = CrifRecord::RiskType::CreditNonQ;
186 vegaRiskType = CrifRecord::RiskType::CreditVolNonQ;
187 break;
188 case RiskClass::Equity:
189 deltaRiskType = CrifRecord::RiskType::Equity;
190 vegaRiskType = CrifRecord::RiskType::EquityVol;
191 break;
192 case RiskClass::Commodity:
193 deltaRiskType = CrifRecord::RiskType::Commodity;
194 vegaRiskType = CrifRecord::RiskType::CommodityVol;
195 break;
196 case RiskClass::FX:
197 deltaRiskType = CrifRecord::RiskType::FX;
198 vegaRiskType = CrifRecord::RiskType::FXVol;
199 break;
200 default:
201 QL_FAIL("riskClassToRiskType: Unexpected risk class");
202 }
203
204 return std::make_pair(deltaRiskType, vegaRiskType);
205}
206
208 switch (rt) {
209 case CrifRecord::RiskType::Commodity:
210 case CrifRecord::RiskType::CommodityVol:
211 return SimmConfiguration::RiskClass::Commodity;
212 case CrifRecord::RiskType::CreditQ:
213 case CrifRecord::RiskType::CreditVol:
214 case CrifRecord::RiskType::BaseCorr:
215 return SimmConfiguration::RiskClass::CreditQualifying;
216 case CrifRecord::RiskType::CreditNonQ:
217 case CrifRecord::RiskType::CreditVolNonQ:
218 return SimmConfiguration::RiskClass::CreditNonQualifying;
219 case CrifRecord::RiskType::Equity:
220 case CrifRecord::RiskType::EquityVol:
221 return SimmConfiguration::RiskClass::Equity;
222 case CrifRecord::RiskType::FX:
223 case CrifRecord::RiskType::FXVol:
224 return SimmConfiguration::RiskClass::FX;
225 case CrifRecord::RiskType::Inflation:
226 case CrifRecord::RiskType::InflationVol:
227 case CrifRecord::RiskType::IRCurve:
228 case CrifRecord::RiskType::IRVol:
229 case CrifRecord::RiskType::XCcyBasis:
230 return SimmConfiguration::RiskClass::InterestRate;
231 default:
232 QL_FAIL("riskTypeToRiskClass: Invalid risk type");
233 }
234}
235
236ostream& operator<<(ostream& out, const SimmConfiguration::SimmSide& s) {
237 string side = s == SimmConfiguration::SimmSide::Call ? "Call" : "Post";
238 return out << side;
239}
240
241ostream& operator<<(ostream& out, const SimmConfiguration::RiskClass& rc) {
242 QL_REQUIRE(riskClassMap.left.count(rc) > 0,
243 "Risk class (" << static_cast<int>(rc) << ") not a valid SimmConfiguration::RiskClass");
244 return out << riskClassMap.left.at(rc);
245}
246
247ostream& operator<<(ostream& out, const SimmConfiguration::MarginType& mt) {
248 QL_REQUIRE(marginTypeMap.left.count(mt) > 0,
249 "Margin type (" << static_cast<int>(mt) << ") not a valid SimmConfiguration::MarginType");
250 return out << marginTypeMap.left.at(mt);
251}
252
253ostream& operator<<(ostream& out, const SimmConfiguration::IMModel& model) {
254 QL_REQUIRE(imModelMap.left.count(model) > 0, "Product class not a valid SimmConfiguration::IMModel");
256 return out << "SIMM";
257 else
258 return out << imModelMap.left.at(model);
259}
260
261ostream& operator<<(ostream& out, const SimmConfiguration::Regulation& regulation) {
262 QL_REQUIRE(regulationsMap.left.count(regulation) > 0, "Product class not a valid SimmConfiguration::Regulation");
263 return out << regulationsMap.left.at(regulation);
264}
265
267 if (side == "Call") {
268 return SimmConfiguration::SimmSide::Call;
269 } else if (side == "Post") {
270 return SimmConfiguration::SimmSide::Post;
271 } else {
272 QL_FAIL("Could not parse the string '" << side << "' to a SimmSide");
273 }
274}
275
277 QL_REQUIRE(riskClassMap.right.count(rc) > 0,
278 "Risk class string " << rc << " does not correspond to a valid SimmConfiguration::RiskClass");
279 return riskClassMap.right.at(rc);
280}
281
283 QL_REQUIRE(marginTypeMap.right.count(mt) > 0,
284 "Margin type string " << mt << " does not correspond to a valid SimmConfiguration::MarginType");
285 return marginTypeMap.right.at(mt);
286}
287
289 for (auto it = imModelMap.right.begin(); it != imModelMap.right.end(); it++) {
290 if (boost::to_lower_copy(model) == boost::to_lower_copy(it->first))
291 return it->second;
292 }
293
294 // If we reach this point, then the IM modelprovided was not found
295 QL_FAIL("IM model string " << model << " does not correspond to a valid SimmConfiguration::IMModel");
296}
297
299 if (regulationsMap.right.count(regulation) == 0) {
300 return SimmConfiguration::Regulation::Invalid;
301 } else {
302 return regulationsMap.right.at(regulation);
303 }
304}
305
306string combineRegulations(const string& regs1, const string& regs2) {
307 if (regs1.empty())
308 return regs2;
309 if (regs2.empty())
310 return regs1;
311
312 return regs1 + ',' + regs2;
313}
314
315set<string> parseRegulationString(const string& regsString, const set<string>& valueIfEmpty) {
316 set<string> uniqueRegNames;
317
318 // "," is a delimiter; "[" and "]" are possible characters, but are not needed in processing
319 vector<string> regNames;
320 boost::split(regNames, regsString, boost::is_any_of(",[] "));
321
322 // Remove any resultant blank strings
323 regNames.erase(std::remove(regNames.begin(), regNames.end(), ""), regNames.end());
324
325 // One last trim for good measure
326 for (string& r : regNames)
327 boost::trim(r);
328
329 // Sort the elements to prevent different permutations of the same regulations being treated differently
330 // e.g. "APRA,USPR" and "USPR,APRA" should ultimately be treated as the same.
331 std::sort(regNames.begin(), regNames.end());
332
333 uniqueRegNames = set<string>(regNames.begin(), regNames.end());
334
335 // If no (valid) regulations provided, we consider the regulation to be unspecified
336 if (uniqueRegNames.empty())
337 return valueIfEmpty;
338 else
339 return uniqueRegNames;
340}
341
342string sortRegulationString(const string& regsString) {
343 // This method will sort the regulations
344 set<string> uniqueRegNames = parseRegulationString(regsString);
345
346 if (uniqueRegNames.size() == 0 ||
347 (uniqueRegNames.size() == 1 && uniqueRegNames.count("Unspecified") > 0)) {
348 return string();
349 } else {
350 return boost::algorithm::join(uniqueRegNames, ",");
351 }
352}
353
354string removeRegulations(const string& regsString, const vector<string>& regsToRemove) {
355 set<string> uniqueRegNames = parseRegulationString(regsString);
356
357 for (const string& r : regsToRemove) {
358 auto it = uniqueRegNames.find(r);
359 if (it != uniqueRegNames.end())
360 uniqueRegNames.erase(it);
361 }
362
363 if (!uniqueRegNames.empty())
364 return boost::algorithm::join(uniqueRegNames, ",");
365 else
366 return string();
367}
368
369string filterRegulations(const string& regsString, const vector<string>& regsToFilter) {
370 set<string> uniqueRegNames = parseRegulationString(regsString);
371 set<string> filteredRegs;
372
373 for (const string& r : uniqueRegNames) {
374 auto it = std::find(regsToFilter.begin(), regsToFilter.end(), r);
375 if (it != regsToFilter.end())
376 filteredRegs.insert(*it);
377 }
378
379 if (!filteredRegs.empty())
380 return boost::algorithm::join(filteredRegs, ",");
381 else
382 return string();
383}
384
385SimmConfiguration::Regulation getWinningRegulation(const std::vector<string>& winningRegulations) {
386
387 vector<SimmConfiguration::Regulation> mappedRegulations;
388
389 for (const string& reg : winningRegulations)
390 mappedRegulations.push_back(parseRegulation(reg));
391
392 SimmConfiguration::Regulation winningRegulation = mappedRegulations.front();
393 for (const auto& reg : mappedRegulations) {
394 if (reg < winningRegulation)
395 winningRegulation = reg;
396 }
397
398 return winningRegulation;
399}
400
401//! Define ordering for ProductClass
403 const CrifRecord::ProductClass& rhs) {
404 QL_REQUIRE(lhs != CrifRecord::ProductClass::All && rhs != CrifRecord::ProductClass::All,
405 "Cannot compare the \"All\" ProductClass.");
406 QL_REQUIRE(static_cast<int>(CrifRecord::ProductClass::All) == 10,
407 "Number of SIMM Product classes is not 11. Some order comparisons are not handled.");
408
409 // all branches end in returns so no breaks are included.
410 switch (lhs) {
411 case CrifRecord::ProductClass::AddOnFixedAmount:
412 case CrifRecord::ProductClass::AddOnNotionalFactor:
413 case CrifRecord::ProductClass::Empty:
414 case CrifRecord::ProductClass::Other:
415 switch (rhs) {
416 case CrifRecord::ProductClass::AddOnFixedAmount:
417 case CrifRecord::ProductClass::AddOnNotionalFactor:
418 case CrifRecord::ProductClass::Empty:
419 case CrifRecord::ProductClass::Other:
420 return false;
421 default:
422 return true;
423 }
424 case CrifRecord::ProductClass::RatesFX:
425 case CrifRecord::ProductClass::Rates:
426 case CrifRecord::ProductClass::FX:
427 switch (rhs) {
428 case CrifRecord::ProductClass::Empty:
429 case CrifRecord::ProductClass::Other:
430 case CrifRecord::ProductClass::RatesFX:
431 case CrifRecord::ProductClass::Rates:
432 case CrifRecord::ProductClass::FX:
433 return false;
434 default:
435 return true;
436 }
437 case CrifRecord::ProductClass::Equity:
438 switch (rhs) {
439 case CrifRecord::ProductClass::Empty:
440 case CrifRecord::ProductClass::Other:
441 case CrifRecord::ProductClass::RatesFX:
442 case CrifRecord::ProductClass::Rates:
443 case CrifRecord::ProductClass::FX:
444 case CrifRecord::ProductClass::Equity:
445 return false;
446 default:
447 return true;
448 }
449 case CrifRecord::ProductClass::Commodity:
450 if (rhs != CrifRecord::ProductClass::Credit) {
451 return false;
452 } else {
453 return true;
454 }
455 case CrifRecord::ProductClass::Credit:
456 return false;
457 case CrifRecord::ProductClass::All:
458 // not handled, fall through to failure
459 break;
460 }
461 QL_FAIL("Unhandled SIMM Product class in waterfall logic.");
462}
463
465 const CrifRecord::ProductClass& rhs) {
466 return SimmConfiguration::less_than(rhs, lhs);
467}
469 const CrifRecord::ProductClass& rhs) {
470 return !SimmConfiguration::greater_than(lhs, rhs);
471}
473 const CrifRecord::ProductClass& rhs) {
474 return !SimmConfiguration::less_than(lhs, rhs);
475}
476} // namespace analytics
477} // namespace ore
static std::set< CrifRecord::RiskType > riskTypes(bool includeAll=false)
Give back a set containing the RiskType values optionally excluding 'All'.
static bool greater_than_or_equal_to(const CrifRecord::ProductClass &lhs, const CrifRecord::ProductClass &rhs)
static const QuantLib::Size numberOfRiskClasses
Number of risk classes including RiskClass::All.
static std::set< CrifRecord::ProductClass > productClasses(bool includeAll=false)
Give back a set containing the ProductClass values optionally excluding 'All'.
static std::set< MarginType > marginTypes(bool includeAll=false)
Give back a set containing the MarginType values optionally excluding 'All'.
SimmSide
Enum indicating the relevant side of the SIMM calculation.
static const QuantLib::Size numberOfMarginTypes
Number of margin types including MarginType::All.
static bool less_than(const CrifRecord::ProductClass &lhs, const CrifRecord::ProductClass &rhs)
Define ordering for ProductClass according to a waterfall:
static bool less_than_or_equal_to(const CrifRecord::ProductClass &lhs, const CrifRecord::ProductClass &rhs)
static std::set< RiskClass > riskClasses(bool includeAll=false)
Give back a set containing the RiskClass values optionally excluding 'All'.
static RiskClass riskTypeToRiskClass(const CrifRecord::RiskType &rt)
For a given rirsk type, return the corresponding risk class.
static const QuantLib::Size numberOfRegulations
Number of regulations.
static std::pair< CrifRecord::RiskType, CrifRecord::RiskType > riskClassToRiskType(const RiskClass &rc)
For a given risk class, return the corresponding risk types.
static bool greater_than(const CrifRecord::ProductClass &lhs, const CrifRecord::ProductClass &rhs)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
string combineRegulations(const string &regs1, const string &regs2)
SimmConfiguration::Regulation parseRegulation(const string &regulation)
SimmConfiguration::SimmSide parseSimmSide(const string &side)
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 &regsString, const set< string > &valueIfEmpty)
Reads a string containing regulations applicable for a given CRIF record.
SimmConfiguration::IMModel parseIMModel(const string &model)
const bm< SimmConfiguration::MarginType > marginTypeMap
SimmConfiguration::RiskClass parseSimmRiskClass(const string &rc)
string filterRegulations(const string &regsString, const vector< string > &regsToFilter)
string removeRegulations(const string &regsString, const vector< string > &regsToRemove)
Removes a given vector of regulations from a string of regulations and returns a string with the regu...
const bm< MarketRiskConfiguration::RiskClass > riskClassMap
Definition: riskfilter.cpp:45
boost::bimap< T, boost::bimaps::set_of< string, string_cmp > > bm
Definition: riskfilter.cpp:42
const bm< SimmConfiguration::IMModel > imModelMap
const bm< SimmConfiguration::Regulation > regulationsMap
SimmConfiguration::MarginType parseSimmMarginType(const string &mt)
string sortRegulationString(const string &regsString)
std::string to_string(const LocationInfo &l)
SIMM configuration interface.