Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
crif.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2023 AcadiaSoft Inc.
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
19/*! \file orea/simm/crif.cpp
20 \brief Struct for holding CRIF records
21*/
22
23#include <boost/range/adaptor/filtered.hpp>
24#include <boost/range/adaptor/transformed.hpp>
25#include <boost/range/algorithm_ext.hpp>
26#include <boost/range/algorithm_ext/erase.hpp>
28#include <orea/simm/crif.hpp>
34
35namespace ore {
36namespace analytics {
37
38auto isSimmParameter = [](const ore::analytics::CrifRecord& x) { return x.isSimmParameter(); };
40
41void Crif::addRecord(const CrifRecord& record, bool aggregateDifferentAmountCurrencies, bool sortFxVolQualifer) {
42 if (record.type() == CrifRecord::RecordType::FRTB) {
43 addFrtbCrifRecord(record, aggregateDifferentAmountCurrencies, sortFxVolQualifer);
44 } else if (record.type() == CrifRecord::RecordType::SIMM && !record.isSimmParameter()) {
45 addSimmCrifRecord(record, aggregateDifferentAmountCurrencies, sortFxVolQualifer);
46 } else {
48 }
49}
50
51void Crif::addFrtbCrifRecord(const CrifRecord& record, bool aggregateDifferentAmountCurrencies,
52 bool sortFxVolQualifer) {
53 QL_REQUIRE(type_ == CrifType::Empty || type_ == CrifType::Frtb, "Can not add a FRTB crif record to a SIMM Crif");
54 if (type_ == CrifType::Empty) {
56 }
57 insertCrifRecord(record, aggregateDifferentAmountCurrencies);
58}
59
60void Crif::addSimmCrifRecord(const CrifRecord& record, bool aggregateDifferentAmountCurrencies,
61 bool sortFxVolQualifer) {
62 QL_REQUIRE(type_ == CrifType::Empty || type_ == CrifType::Simm, "Can not add a Simm crif record to a Frtb Crif");
63 if (type_ == CrifType::Empty) {
65 }
66 auto recordToAdd = record;
67 if (sortFxVolQualifer && recordToAdd.riskType == CrifRecord::RiskType::FXVol) {
68 auto ccy_1 = recordToAdd.qualifier.substr(0, 3);
69 auto ccy_2 = recordToAdd.qualifier.substr(3);
70 if (ccy_1 > ccy_2)
71 ccy_1.swap(ccy_2);
72 recordToAdd.qualifier = ccy_1 + ccy_2;
73 }
74 insertCrifRecord(recordToAdd, aggregateDifferentAmountCurrencies);
75}
76
77void Crif::insertCrifRecord(const CrifRecord& record, bool aggregateDifferentAmountCurrencies) {
78
79 auto it = aggregateDifferentAmountCurrencies ? records_.end() : records_.find(record);
80 auto itDiffAmountCcy = aggregateDifferentAmountCurrencies
83
84 if (it == records_.end() && itDiffAmountCcy == diffAmountCurrenciesIndex_.end()) {
85 auto recordIt = records_.insert(record);
86 diffAmountCurrenciesIndex_[record.getSimmAmountCcyKey()] = &(*(recordIt.first));
87 portfolioIds_.insert(record.portfolioId);
89 } else if (it != records_.end()) {
91 } else {
92 updateAmountExistingRecord(itDiffAmountCcy, record);
93 }
94}
95
97 auto it = records_.find(record);
98 if (it == records_.end()) {
99 CrifRecord newRecord = record;
100 records_.insert(newRecord);
101 diffAmountCurrenciesIndex_[record.getSimmAmountCcyKey()] = &newRecord;
102 } else if (it->riskType == CrifRecord::RiskType::AddOnFixedAmount) {
103 updateAmountExistingRecord(it, record);
104 } else if (it->riskType == CrifRecord::RiskType::AddOnNotionalFactor ||
105 it->riskType == CrifRecord::RiskType::ProductClassMultiplier) {
106 // Only log warning if the values are not the same. If they are, then there is no material discrepancy.
107 if (record.amount != it->amount) {
108 string errMsg = "Found more than one instance of risk type " + ore::data::to_string(it->riskType) +
109 ". Please check the SIMM parameters input. If enforceIMRegulations=False, then it "
110 "is possible that multiple entries for different regulations now belong under the same "
111 "'Unspecified' regulation.";
112 ore::analytics::StructuredAnalyticsWarningMessage("SIMM", "Aggregating SIMM parameters", errMsg).log();
113 }
114 }
115}
116
117void Crif::updateAmountExistingRecord(std::set<CrifRecord>::iterator& it, const CrifRecord& record) {
118 bool updated = false;
119 if (record.hasAmountUsd()) {
120 it->amountUsd += record.amountUsd;
121 updated = true;
122 }
123 if (record.hasAmount() && record.hasAmountCcy() && it->amountCurrency == record.amountCurrency) {
124 it->amount += record.amount;
125 updated = true;
126 }
127 if (record.hasAmountResultCcy() && record.hasResultCcy() && it->resultCurrency == record.resultCurrency) {
128 it->amountResultCcy += record.amountResultCcy;
129 updated = true;
130 }
131 if (updated)
132 DLOG("Updated net CRIF records: " << *it)
133}
134
135void Crif::updateAmountExistingRecord(std::map<CrifRecord::SimmAmountCcyKey, const CrifRecord*>::iterator& it,
136 const CrifRecord& record) {
137 bool updated = false;
138 if (record.hasAmountUsd()) {
139 it->second->amountUsd += record.amountUsd;
140 updated = true;
141 }
142 if (record.hasAmount() && record.hasAmountCcy() && it->second->amountCurrency == record.amountCurrency) {
143 it->second->amount += record.amount;
144 updated = true;
145 }
146 if (record.hasAmountResultCcy() && record.hasResultCcy() && it->second->resultCurrency == record.resultCurrency) {
147 it->second->amountResultCcy += record.amountResultCcy;
148 updated = true;
149 }
150 if (updated)
151 DLOG("Updated net CRIF records: " << *(it->second))
152}
153
154void Crif::addRecords(const Crif& crif, bool aggregateDifferentAmountCurrencies, bool sortFxVolQualifer) {
155 for (const auto& r : crif.records_) {
156 addRecord(r, aggregateDifferentAmountCurrencies, sortFxVolQualifer);
157 }
158}
159
161 Crif result;
162 for (auto cr : records_) {
163 // We set the trade ID to an empty string because we are netting at portfolio level
164 // The only exception here is schedule trades that are denoted by two rows,
165 // with RiskType::Notional and RiskType::PV
166 if (cr.imModel != "Schedule") {
167 cr.tradeId = "";
168 }
169 result.addRecord(cr);
170 }
171 return result;
172}
173
174const bool Crif::hasCrifRecords() const {
175 auto it = std::find_if(records_.begin(), records_.end(), isNotSimmParameter);
176 return it != records_.end();
177}
178
179//! check if the Crif contains simmParameters
180const bool Crif::hasSimmParameters() const {
181 auto it = std::find_if(records_.begin(), records_.end(), isSimmParameter);
182 return it != records_.end();
183}
184
185//! returns a Crif containing only simmParameter entries
187 Crif results;
188 for (const auto& record : records_) {
189 if (record.isSimmParameter()) {
190 results.addSimmParameterRecord(record);
191 }
192 }
193 return results;
194}
195//! Find first element
196std::set<CrifRecord>::const_iterator Crif::findBy(const NettingSetDetails nsd, CrifRecord::ProductClass pc,
197 const CrifRecord::RiskType rt, const std::string& qualifier) const {
198 return std::find_if(records_.begin(), records_.end(), [&nsd, &pc, &rt, &qualifier](const CrifRecord& record) {
199 return record.nettingSetDetails == nsd && record.productClass == pc && record.riskType == rt &&
200 record.qualifier == qualifier;
201 });
202};
203
204Crif Crif::filterNonZeroAmount(double threshold, std::string alwaysIncludeFxRiskCcy) const {
205 Crif results;
206 for (auto record : records_) {
207 QL_REQUIRE(record.amount != QuantLib::Null<QuantLib::Real>() || record.amountUsd != QuantLib::Null<double>(),
208 "Internal Error, amount and amountUsd are empty");
209 double absAmount = 0.0;
210 if ((record.amount != QuantLib::Null<double>()) && (record.amountUsd != QuantLib::Null<double>())) {
211 absAmount = std::max(std::fabs(record.amount), std::fabs(record.amountUsd));
212 } else if (record.amount != QuantLib::Null<double>()) {
213 absAmount = std::fabs(record.amount);
214 } else if (record.amountUsd != QuantLib::Null<double>()) {
215 absAmount = std::fabs(record.amountUsd);
216 }
217 bool add = (absAmount > threshold && !QuantLib::close_enough(absAmount, threshold));
218 if (!alwaysIncludeFxRiskCcy.empty())
219 add = add || (record.riskType == CrifRecord::RiskType::FX && record.qualifier == alwaysIncludeFxRiskCcy);
220 if (add) {
221 results.addRecord(record);
222 }
223 }
224 return results;
225}
226
228 const CrifRecord::RiskType rt) const {
229 auto res = records_ | boost::adaptors::filtered([&nsd, &pc, &rt](const CrifRecord& record) {
230 return record.nettingSetDetails == nsd && record.productClass == pc && record.riskType == rt;
231 }) |
232 boost::adaptors::transformed([](const CrifRecord& record) { return record.qualifier; });
233 return boost::copy_range<std::set<std::string>>(res);
234}
235
236std::vector<CrifRecord> Crif::filterByQualifierAndBucket(const NettingSetDetails& nsd,
238 const CrifRecord::RiskType rt, const std::string& qualifier,
239 const std::string& bucket) const {
240 return boost::copy_range<std::vector<CrifRecord>>(
241 records_ | boost::adaptors::filtered([&nsd, &pc, &rt, &qualifier, &bucket](const CrifRecord& record) {
242 return record.nettingSetDetails == nsd && record.productClass == pc && record.riskType == rt &&
243 record.qualifier == qualifier && record.bucket == bucket;
244 }));
245}
246
247std::vector<CrifRecord> Crif::filterByQualifier(const NettingSetDetails& nsd, const CrifRecord::ProductClass pc,
248 const CrifRecord::RiskType rt, const std::string& qualifier) const {
249
250 return boost::copy_range<std::vector<CrifRecord>>(
251 records_ | boost::adaptors::filtered([&nsd, &pc, &rt, &qualifier](const CrifRecord& record) {
252 return record.nettingSetDetails == nsd && record.productClass == pc && record.riskType == rt &&
253 record.qualifier == qualifier;
254 }));
255}
256
257std::vector<CrifRecord> Crif::filterByBucket(const NettingSetDetails& nsd, const CrifRecord::ProductClass pc,
258 const CrifRecord::RiskType rt, const std::string& bucket) const {
259 return boost::copy_range<std::vector<CrifRecord>>(
260 records_ | boost::adaptors::filtered([&nsd, &pc, &rt, &bucket](const CrifRecord& record) {
261 return record.nettingSetDetails == nsd && record.productClass == pc && record.riskType == rt &&
262 record.bucket == bucket;
263 }));
264}
265
266std::vector<CrifRecord> Crif::filterBy(const NettingSetDetails& nsd, const CrifRecord::ProductClass pc,
267 const CrifRecord::RiskType rt) const {
268 return boost::copy_range<std::vector<CrifRecord>>(
269 records_ | boost::adaptors::filtered([&nsd, &pc, &rt](const CrifRecord& record) {
270 return record.nettingSetDetails == nsd && record.productClass == pc && record.riskType == rt;
271 }));
272}
273
274std::vector<CrifRecord> Crif::filterBy(const CrifRecord::RiskType rt) const {
275 return boost::copy_range<std::vector<CrifRecord>>(
276 records_ | boost::adaptors::filtered([&rt](const CrifRecord& record) { return record.riskType == rt; }));
277}
278
279std::vector<CrifRecord> Crif::filterByTradeId(const std::string& id) const {
280 return boost::copy_range<std::vector<CrifRecord>>(
281 records_ | boost::adaptors::filtered([&id](const CrifRecord& record) { return record.tradeId == id; }));
282}
283
284std::set<std::string> Crif::tradeIds() const {
285 return boost::copy_range<std::set<std::string>>(
286 records_ | boost::adaptors::transformed([](const CrifRecord& r) { return r.tradeId; }));
287}
288
289//! deletes all existing simmParameter and replaces them with the new one
290void Crif::setSimmParameters(const Crif& crif) {
291 auto backup = records_;
292 records_.clear();
293 for (auto& r : backup) {
294 if (!r.isSimmParameter()) {
295 addRecord(r);
296 }
297 }
298 for (const auto& r : crif) {
299 if (r.isSimmParameter()) {
301 }
302 }
303}
304
305void Crif::setCrifRecords(const Crif& crif) {
306 auto backup = records_;
307 records_.clear();
308 for (auto& r : backup) {
309 if (r.isSimmParameter()) {
310 addRecord(r);
311 }
312 }
313 for (const auto& r : crif) {
314 if (!r.isSimmParameter()) {
315 addRecord(r);
316 }
317 }
318}
319
320//! Give back the set of portfolio IDs that have been loaded
321const std::set<std::string>& Crif::portfolioIds() const { return portfolioIds_; }
322const std::set<NettingSetDetails>& Crif::nettingSetDetails() const { return nettingSetDetails_; }
323
324std::set<CrifRecord::ProductClass> Crif::ProductClassesByNettingSetDetails(const NettingSetDetails nsd) const {
325 std::set<CrifRecord::ProductClass> keys;
326 for (const auto& record : records_) {
327 if (record.nettingSetDetails == nsd) {
328 keys.insert(record.productClass);
329 }
330 }
331 return keys;
332}
333
335 const CrifRecord::RiskType rt, const std::string& qualifier) const {
336 return std::count_if(records_.begin(), records_.end(), [&nsd, &pc, &rt, &qualifier](const CrifRecord& record) {
337 return record.nettingSetDetails == nsd && record.productClass == pc && record.riskType == rt &&
338 record.qualifier == qualifier;
339 });
340}
341
343 bool hasNettingSetDetails = false;
344 for (const auto& nsd : nettingSetDetails_) {
345 if (!nsd.emptyOptionalFields())
347 }
349}
350
351void Crif::fillAmountUsd(const QuantLib::ext::shared_ptr<ore::data::Market> market) {
352 if (!market) {
353 WLOG("CrifLoader::fillAmountUsd() was called, but market object is empty.")
354 return;
355 }
356 std::set<CrifRecord> results;
357
358 for (const CrifRecord& r : records_) {
359 auto cr = r;
360 // Fill in amount USD if it is missing and if CRIF record requires it (i.e. if it has amount and amount
361 // currency, and risk type is neither AddOnNotionalFactor or ProductClassMultiplier)
362 if (cr.requiresAmountUsd() && !cr.hasAmountUsd()) {
363 if (!cr.hasAmount() || !cr.hasAmountCcy()) {
365 cr.tradeId, cr.tradeType, "Populating CRIF amount USD",
366 "CRIF record is missing one of Amount and AmountCurrency, and there is no amountUsd value to "
367 "fall back to: " +
369 .log();
370 } else {
371 double usdSpot = market->fxRate(cr.amountCurrency + "USD")->value();
372 cr.amountUsd = cr.amount * usdSpot;
373 }
374 }
375 results.insert(cr);
376 }
377 records_ = results;
378}
379
380} // namespace analytics
381} // namespace ore
Crif aggregate() const
Aggregate all existing records.
Definition: crif.cpp:160
void setCrifRecords(const Crif &crif)
deletes all existing simmParameter and replaces them with the new one
Definition: crif.cpp:305
void updateAmountExistingRecord(std::set< CrifRecord >::iterator &it, const CrifRecord &record)
Definition: crif.cpp:117
std::set< std::string > qualifiersBy(const NettingSetDetails nsd, CrifRecord::ProductClass pc, const CrifRecord::RiskType rt) const
Definition: crif.cpp:227
std::set< ore::data::NettingSetDetails > nettingSetDetails_
Definition: crif.hpp:132
std::vector< CrifRecord > filterByQualifier(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &qualifier) const
Definition: crif.cpp:247
std::set< std::string > tradeIds() const
Definition: crif.cpp:284
const std::set< ore::data::NettingSetDetails > & nettingSetDetails() const
Definition: crif.cpp:322
Crif filterNonZeroAmount(double threshold=0.0, std::string alwaysIncludeFxRiskCcy="") const
returns a crif without zero amount records, FXRisk entries in currency alwaysIncludeFxRiskCcy are alw...
Definition: crif.cpp:204
void addFrtbCrifRecord(const CrifRecord &record, bool aggregateDifferentAmountCurrencies=false, bool sortFxVolQualifer=true)
Definition: crif.cpp:51
std::vector< CrifRecord > filterByBucket(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &bucket) const
Definition: crif.cpp:257
std::vector< CrifRecord > filterByQualifierAndBucket(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &qualifier, const std::string &bucket) const
Definition: crif.cpp:236
std::set< CrifRecord::ProductClass > ProductClassesByNettingSetDetails(const NettingSetDetails nsd) const
Definition: crif.cpp:324
std::vector< CrifRecord > filterByTradeId(const std::string &id) const
Definition: crif.cpp:279
void addRecords(const Crif &crif, bool aggregateDifferentAmountCurrencies=false, bool sortFxVolQualfier=true)
Definition: crif.cpp:154
Crif simmParameters() const
returns a Crif containing only simmParameter entries
Definition: crif.cpp:186
const std::set< std::string > & portfolioIds() const
Give back the set of portfolio IDs that have been loaded.
Definition: crif.cpp:321
void addRecord(const CrifRecord &record, bool aggregateDifferentAmountCurrencies=false, bool sortFxVolQualifer=true)
Definition: crif.cpp:41
size_t countMatching(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &qualifier) const
Definition: crif.cpp:334
bool hasNettingSetDetails() const
Check if netting set details are used anywhere, instead of just the netting set ID.
Definition: crif.cpp:342
const bool hasCrifRecords() const
check if there are crif records beside simmParameters
Definition: crif.cpp:174
std::set< std::string > portfolioIds_
Set of portfolio IDs that have been loaded.
Definition: crif.hpp:131
std::set< CrifRecord > records_
Definition: crif.hpp:126
void insertCrifRecord(const CrifRecord &record, bool aggregateDifferentAmountCurrencies=false)
Definition: crif.cpp:77
void fillAmountUsd(const QuantLib::ext::shared_ptr< ore::data::Market > market)
Definition: crif.cpp:351
const bool hasSimmParameters() const
check if the Crif contains simmParameters
Definition: crif.cpp:180
std::set< CrifRecord >::const_iterator findBy(const NettingSetDetails nsd, CrifRecord::ProductClass pc, const CrifRecord::RiskType rt, const std::string &qualifier) const
Find first element.
Definition: crif.cpp:196
CrifType type_
Definition: crif.hpp:125
std::map< CrifRecord::SimmAmountCcyKey, const CrifRecord * > diffAmountCurrenciesIndex_
Definition: crif.hpp:127
std::vector< CrifRecord > filterBy(const NettingSetDetails &nsd, const CrifRecord::ProductClass pc, const CrifRecord::RiskType rt) const
Definition: crif.cpp:266
void addSimmParameterRecord(const CrifRecord &record)
Definition: crif.cpp:96
void setSimmParameters(const Crif &crif)
deletes all existing simmParameter and replaces them with the new one
Definition: crif.cpp:290
void addSimmCrifRecord(const CrifRecord &record, bool aggregateDifferentAmountCurrencies=false, bool sortFxVolQualifer=true)
Definition: crif.cpp:60
Struct for holding CRIF records.
#define DLOG(text)
#define WLOG(text)
auto isNotSimmParameter
Definition: crif.cpp:39
auto isSimmParameter
Definition: crif.cpp:38
std::string to_string(const LocationInfo &l)
RecordType type() const
Definition: crifrecord.cpp:186
const SimmAmountCcyKey getSimmAmountCcyKey() const
Definition: crifrecord.hpp:294
QuantLib::Real amountResultCcy
Definition: crifrecord.hpp:148
NettingSetDetails nettingSetDetails
Definition: crifrecord.hpp:156
bool hasAmountResultCcy() const
Definition: crifrecord.hpp:208
Class for structured analytics warnings.