Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
imschedulecalculator.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2022 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
24
29
31#include <ql/math/comparison.hpp>
32#include <ql/time/daycounters/actualactual.hpp>
33
34using std::map;
35using std::pair;
36using std::max;
37using std::min;
38using std::string;
39using std::vector;
40using QuantLib::close_enough;
41
47using QuantLib::Real;
48using QuantLib::Null;
49
50namespace ore {
51namespace analytics {
52
53IMScheduleCalculator::IMScheduleCalculator(const Crif& crif, const string& calculationCcy,
54 const QuantLib::ext::shared_ptr<ore::data::Market> market,
55 const bool determineWinningRegulations, const bool enforceIMRegulations,
56 const bool quiet,
57 const map<SimmSide, set<NettingSetDetails>>& hasSEC,
58 const map<SimmSide, set<NettingSetDetails>>& hasCFTC)
59 : crif_(crif), calculationCcy_(calculationCcy), market_(market), quiet_(quiet),
60 hasSEC_(hasSEC), hasCFTC_(hasCFTC) {
61
63 "The calculation currency (" << calculationCcy_ << ") must be a valid ISO currency code");
64
65 QuantLib::Date today = QuantLib::Settings::instance().evaluationDate();
66 QuantLib::DayCounter dayCounter = QuantLib::ActualActual(QuantLib::ActualActual::ISDA);
67
68 // Collect Schedule CRIF records
69 Crif tmp;
70 for (const CrifRecord& cr : crif_) {
71 const bool isSchedule = cr.imModel == "Schedule";
72
73 if (!isSchedule) {
74 if (determineWinningRegulations) {
76 cr.tradeId, cr.tradeType, "IM Schedule calculator",
77 "Skipping over CRIF record without im_model=Schedule for portfolio [ " +
78 to_string(cr.nettingSetDetails) + "]")
79 .log();
80 }
81 continue;
82 }
83
84 // Check for each netting set whether post/collect regs are populated at all
85 if (collectRegsIsEmpty_.find(cr.nettingSetDetails) == collectRegsIsEmpty_.end()) {
86 collectRegsIsEmpty_[cr.nettingSetDetails] = cr.collectRegulations.empty();
87 } else if (collectRegsIsEmpty_.at(cr.nettingSetDetails) && !cr.collectRegulations.empty()) {
88 collectRegsIsEmpty_.at(cr.nettingSetDetails) = false;
89 }
90 if (postRegsIsEmpty_.find(cr.nettingSetDetails) == postRegsIsEmpty_.end()) {
91 postRegsIsEmpty_[cr.nettingSetDetails] = cr.postRegulations.empty();
92 } else if (postRegsIsEmpty_.at(cr.nettingSetDetails) && !cr.postRegulations.empty()) {
93 postRegsIsEmpty_.at(cr.nettingSetDetails) = false;
94 }
95
96 tmp.addRecord(cr);
97 }
98 crif_ = tmp;
99
100
101 // Separate out CRIF records by regulations and collect per-trade data
102 LOG("IMScheduleCalculator: Collecting CRIF trade data");
103 for (const auto& crifRecord : crif_)
104 collectTradeData(crifRecord, enforceIMRegulations);
105
106 // Remove (or modify) trades with incomplete data
107 for (auto& sv : nettingSetRegTradeData_) {
108 const SimmSide& side = sv.first;
109
110 for (auto& nv : sv.second) {
111 const NettingSetDetails& nsd = nv.first;
112
113 for (auto& rv : nv.second) {
114 const string& regulation = rv.first;
115 auto& tradeDataMap = rv.second;
116
117 // Remove (or modify) trades with incomplete Schedule data
118 set<string> tradesToRemove;
119 for (auto& td : tradeDataMap) {
120 if (td.second.incomplete()) {
121 auto subFields = map<string, string>({{"tradeId", td.first}});
122 // If missing PV, assume PV = 0
123 if (td.second.missingPVData()) {
124 td.second.presentValue = 0.0;
125 td.second.presentValueUsd = 0.0;
126 td.second.presentValueCcy = td.second.notionalCcy;
127 }
128 // If missing Notional, do not process the trade
129 if (td.second.missingNotionalData()) {
131 "IMSchedule", "Incomplete CRIF trade data",
132 "Missing Notional data. The trade will not be processed.", subFields)
133 .log();
134 tradesToRemove.insert(td.first);
135 }
136 }
137 }
138
139 for (const string& tid : tradesToRemove) {
140 tradeDataMap.erase(tid);
141 tradeIds_.at(side).at(nsd).at(regulation).erase(tid);
142 }
143
144 // Calculate Schedule data for each trade data obj
145 for (auto& td : tradeDataMap) {
146 IMScheduleTradeData& tradeData = td.second;
147
148 // Calculate gross IM for each IM Schedule trade
149 tradeData.maturity = dayCounter.yearFraction(today, tradeData.endDate);
150 tradeData.label = label(tradeData.productClass, tradeData.maturity);
151 tradeData.labelString = labelString(tradeData.label);
152 tradeData.multiplier = multiplier(tradeData.label);
153 tradeData.grossMarginUsd = tradeData.multiplier * tradeData.notionalUsd;
154
155 // Convert some trade data values into calculation currency
156 const Real usdSpot = calculationCcy_ != "USD" ? market_->fxRate(calculationCcy_ + "USD")->value() : 1.0;
157 tradeData.notionalCalc = tradeData.notionalUsd / usdSpot;
158 tradeData.presentValueCalc = tradeData.presentValueUsd / usdSpot;
159 tradeData.grossMarginCalc = tradeData.grossMarginUsd / usdSpot;
160 if (side == SimmSide::Call)
161 tradeData.collectRegulations = regulation;
162 if (side == SimmSide::Post)
163 tradeData.postRegulations = regulation;
164 }
165 }
166 }
167 }
168
169 // Some additional processing depending on the regulations applicable to each netting set
170 for (auto& sv : nettingSetRegTradeData_) {
171 const SimmSide& side = sv.first;
172
173 for (auto& s : sv.second) {
174 const NettingSetDetails& nettingDetails = s.first;
175
176 // Where there is SEC and CFTC in the portfolio, we add the CFTC trades to SEC,
177 // but still continue with CFTC calculations
178 const bool hasCFTCGlobal = hasCFTC_.at(side).find(nettingDetails) != hasCFTC_.at(side).end();
179 const bool hasSECGlobal = hasSEC_.at(side).find(nettingDetails) != hasSEC_.at(side).end();
180 const bool hasCFTCLocal = s.second.count("CFTC") > 0;
181 const bool hasSECLocal = s.second.count("SEC") > 0;
182
183 if ((hasSECLocal && hasCFTCLocal) || (hasCFTCGlobal && hasSECGlobal)) {
184 if (!hasSECLocal && !hasCFTCLocal)
185 continue;
186
187 if (hasCFTCLocal) {
188 // At this point, we expect to have CFTC trade data at least for the netting set
189 const map<string, IMScheduleTradeData>& tradeDataMapCFTC = s.second.at("CFTC");
190 map<string, IMScheduleTradeData>& tradeDataMapSEC = s.second["SEC"];
191 for (const auto& kv : tradeDataMapCFTC) {
192 // Only add CFTC records to SEC if the record was not already in SEC,
193 // i.e. we skip over CRIF records with regulations specified as e.g. "..., CFTC, SEC, ..."
194 if (tradeDataMapSEC.find(kv.first) == tradeDataMapSEC.end()) {
195 tradeDataMapSEC[kv.first] = kv.second;
196 }
197 }
198 }
199 }
200
201 // If netting set has "Unspecified" plus other regulations, the "Unspecified" sensis are to be excluded.
202 // If netting set only has "Unspecified", then no regulations were ever specified, so all trades are
203 // included.
204 if (s.second.count("Unspecified") > 0 && s.second.size() > 1)
205 s.second.erase("Unspecified");
206 }
207 }
208
209 // Calculate the higher level margins
210 LOG("IMScheduleCalculator: Populating higher level results")
211 for (const auto& sv : nettingSetRegTradeData_) {
212 const SimmSide side = sv.first;
213
214 for (const auto& nv : sv.second) {
215 const NettingSetDetails& nsd = nv.first;
216
217 for (const auto& rv : nv.second) {
218 const string& regulation = rv.first;
219 populateResults(nsd, regulation, side);
220 }
221 }
222 }
223
224 if (determineWinningRegulations) {
225 LOG("IMScheduleCalculator: Determining winning regulations");
226
227 // Determine winning call and post regulations
228 for (const auto& sv : imScheduleResults_) {
229 const SimmSide side = sv.first;
230
231 // Determine winning regulation for each netting set
232 // Collect margin amounts and determine the highest margin amount
233 for (const auto& nv : sv.second) {
234 const NettingSetDetails& nsd = nv.first;
235 Real winningMargin = std::numeric_limits<Real>::min();
236 map<string, Real> nettingSetMargins;
237 vector<Real> margins;
238
239 for (const auto& rv : nv.second) {
240 const string& regulation = rv.first;
241 const IMScheduleResults& schResult = rv.second;
242 const Real& im = schResult.get(ProductClass::All).scheduleIM;
243
244 nettingSetMargins[regulation] = im;
245 if (im > winningMargin)
246 winningMargin = im;
247 }
248
249 // Determine winning regulations, i.e. regulations under which we find the highest margin amount
250 vector<string> winningRegulations;
251 for (const auto& kv : nettingSetMargins) {
252 if (close_enough(kv.second, winningMargin))
253 winningRegulations.push_back(kv.first);
254 }
255
256 // In the case of multiple winning regulations, pick one based on the priority in the list
257 //const Regulation winningRegulation = getWinningRegulation(winningRegulations);
258 string winningRegulation = winningRegulations.size() > 1
260 : winningRegulations.at(0);
261
262 // Populate internal list of winning regulators
263 winningRegulations_[side][nsd] = ore::data::to_string(winningRegulation);
264 }
265 }
266
268 }
269}
270
272 const NettingSetDetails& nettingSetDetails) const {
273 const auto& subWinningRegs = winningRegulations(side);
274 QL_REQUIRE(subWinningRegs.find(nettingSetDetails) != subWinningRegs.end(),
275 "IMScheduleCalculator::winningRegulations(): Could not find netting set in the list of "
276 << side << " schedule IM winning regulations: " << nettingSetDetails);
277 return subWinningRegs.at(nettingSetDetails);
278}
279
280const map<NettingSetDetails, string>& IMScheduleCalculator::winningRegulations(const SimmSide& side) const {
281 QL_REQUIRE(winningRegulations_.find(side) != winningRegulations_.end(),
282 "IMScheduleCalculator::winningRegulations(): Could not find list of"
283 << side << " schedule IM winning regulations");
284 return winningRegulations_.at(side);
285}
286
287const map<SimmConfiguration::SimmSide, map<NettingSetDetails, string>>&
289 return winningRegulations_;
290}
291
292const map<string, IMScheduleResults>& IMScheduleCalculator::imScheduleSummaryResults(const SimmSide& side, const NettingSetDetails& nsd) const {
293 const auto& subResults = imScheduleSummaryResults(side);
294 QL_REQUIRE(subResults.find(nsd) != subResults.end(),
295 "IMScheduleCalculator::imScheduleSummaryResults(): Could not find netting set in the "
296 << side << " IM schedule results: " << nsd);
297 return subResults.at(nsd);
298}
299
300const map<NettingSetDetails, map<string, IMScheduleResults>>& IMScheduleCalculator::imScheduleSummaryResults(const SimmSide& side) const {
301 QL_REQUIRE(imScheduleResults_.find(side) != imScheduleResults_.end(),
302 "IMScheduleCalculator::imScheduleSummaryResults(): Could not find " << side
303 << " IM in the IM Schedule results");
304 return imScheduleResults_.at(side);
305}
306
307const map<SimmConfiguration::SimmSide, map<NettingSetDetails, map<string, IMScheduleResults>>>&
309 return imScheduleResults_;
310};
311
312const pair<string, IMScheduleResults>& IMScheduleCalculator::finalImScheduleSummaryResults(const SimmSide& side,
313 const NettingSetDetails& nsd) const {
314 const auto& subResults = finalImScheduleSummaryResults(side);
315 QL_REQUIRE(subResults.find(nsd) != subResults.end(),
316 "IMScheduleCalculator::finalImScheduleSummaryResults(): Could not find netting set in the final IM Schedule "
317 << side << " results: " << nsd);
318 return subResults.at(nsd);
319}
320
321const map<NettingSetDetails, pair<string, IMScheduleResults>>& IMScheduleCalculator::finalImScheduleSummaryResults(const SimmSide& side) const {
322 QL_REQUIRE(finalImScheduleResults_.find(side) != finalImScheduleResults_.end(),
323 "IMScheduleCalculator::finalImScheduleSummaryResults(): Could not find "
324 << side << " IM in the final IM Schedule results");
325 return finalImScheduleResults_.at(side);
326}
327
328const vector<IMScheduleCalculator::IMScheduleTradeData>& IMScheduleCalculator::imScheduleTradeResults(const string& tradeId) const {
329 QL_REQUIRE(finalTradeData_.find(tradeId) != finalTradeData_.end(),
330 "IMScheduleCalculator::imScheduleTradeResults(): Could not find results for trade: " << tradeId);
331 return finalTradeData_.at(tradeId);
332}
333
334const map<string, vector<IMScheduleCalculator::IMScheduleTradeData>>& IMScheduleCalculator::imScheduleTradeResults() const {
335 return finalTradeData_;
336}
337
338const IMScheduleLabel IMScheduleCalculator::label(const ProductClass& pc, const Real& maturity) {
339 if (pc == ProductClass::Credit) {
340 if (maturity >= 0.0 && maturity < 2.0) {
342 } else if (maturity < 5.0) {
344 } else {
346 }
347
348 } else if (pc == ProductClass::Commodity) {
350
351 } else if (pc == ProductClass::Equity) {
353
354 } else if (pc == ProductClass::FX) {
355 return IMScheduleLabel::FX;
356
357 } else if (pc == ProductClass::Rates) {
358 if (maturity >= 0.0 && maturity < 2.0) {
360 } else if (maturity < 5.0) {
362 } else {
364 }
365
366 } else if (pc == ProductClass::Other) {
368 } else {
369 QL_FAIL("IMSchedule::label() Invalid product class " << pc);
370 }
371}
372
374 const map<IMScheduleLabel, string> labelStringMap_ = map<IMScheduleLabel, string>({
375 {IMScheduleLabel::Credit2, "Credit 0-2 years"},
376 {IMScheduleLabel::Credit5, "Credit 2-5 years"},
377 {IMScheduleLabel::Credit100, "Credit 5+ years"},
378 {IMScheduleLabel::Commodity, "Commodity"},
379 {IMScheduleLabel::Equity, "Equity"},
380 {IMScheduleLabel::FX, "FX"},
381 {IMScheduleLabel::Rates2, "Interest Rate 0-2 years"},
382 {IMScheduleLabel::Rates5, "Interest Rate 2-5 years"},
383 {IMScheduleLabel::Rates100, "Interest Rate 5+ years"},
384 {IMScheduleLabel::Other, "Other"},
385 });
386
387 return labelStringMap_.at(label);
388}
389
390void IMScheduleCalculator::collectTradeData(const CrifRecord& cr, const bool enforceIMRegulations) {
391
392 DLOG("Processing CRIF record for IMSchedule calculation: trade ID \'"
393 << cr.tradeId << "\', portfolio [" << cr.nettingSetDetails << "], product class " << cr.productClass
394 << ", risk type " << cr.riskType << ", end date " << cr.endDate);
395 QL_REQUIRE(cr.riskType == RiskType::PV || cr.riskType == RiskType::Notional,
396 "Unexpected risk type found in CRIF " << cr.riskType << " for trade ID " << cr.tradeId);
397
398 for (const auto& side : {SimmSide::Call, SimmSide::Post}) {
399 const NettingSetDetails& nettingSetDetails = cr.nettingSetDetails;
400
401 bool collectRegsIsEmpty = false;
402 bool postRegsIsEmpty = false;
404 collectRegsIsEmpty = collectRegsIsEmpty_.at(cr.nettingSetDetails);
406 postRegsIsEmpty = postRegsIsEmpty_.at(cr.nettingSetDetails);
407
408 string regsString;
409 if (enforceIMRegulations)
410 regsString = side == SimmSide::Call ? cr.collectRegulations : cr.postRegulations;
411 set<string> regs = parseRegulationString(regsString);
412
413 for (const string& r : regs) {
414 if (r == "Unspecified" && enforceIMRegulations && !(collectRegsIsEmpty && postRegsIsEmpty)) {
415 continue;
416 } else if (r != "Excluded") {
417 // Keep a record of trade IDs for each regulation
418 tradeIds_[side][nettingSetDetails][r].insert(cr.tradeId);
419
420 auto& tradeDataMap = nettingSetRegTradeData_[side][nettingSetDetails][r];
421 auto it = tradeDataMap.find(cr.tradeId);
422 if (it != tradeDataMap.end()) {
423 IMScheduleTradeData& tradeData = it->second;
424
425 QL_REQUIRE(cr.productClass == tradeData.productClass, "Product class is not matching for trade ID "
426 << cr.tradeId << ": " << cr.productClass
427 << " and " << tradeData.productClass);
428 QuantLib::Date incomingDate = parseDate(cr.endDate);
429 QL_REQUIRE(incomingDate == tradeData.endDate, "End date is not matching for trade ID "
430 << cr.tradeId << ": " << incomingDate << " and "
431 << tradeData.endDate);
432 if (cr.riskType == RiskType::PV) {
433 QL_REQUIRE(tradeData.missingPVData(), "Adding PV data for trade that already has PV data, i.e. "
434 "multiple PV records found for the same trade: "
435 << tradeData.tradeId);
436 tradeData.presentValue = cr.amount;
437 tradeData.presentValueUsd = cr.amountUsd;
438 tradeData.presentValueCcy = cr.amountCurrency;
439 } else {
440 QL_REQUIRE(tradeData.missingNotionalData(),
441 "Adding Notional data for trade that already has PV data, i.e. "
442 "multiple Notional records found for the same trade: "
443 << tradeData.tradeId);
444 tradeData.notional = cr.amount;
445 tradeData.notionalUsd = cr.amountUsd;
446 tradeData.notionalCcy = cr.amountCurrency;
447 }
448 } else {
449 const string collectRegs = side == SimmSide::Call ? cr.collectRegulations : "";
450 const string postRegs = side == SimmSide::Post ? cr.postRegulations : "";
451 tradeDataMap.insert(
454 parseDate(cr.endDate), calculationCcy_, collectRegs, postRegs)});
455 }
456 }
457 }
458 }
459}
460
461void IMScheduleCalculator::populateResults(const NettingSetDetails& nettingSetDetails, const string& regulation,
462 const SimmSide& side) {
463
464 LOG("IMScheduleCalculator: Populating " << side << " IM for netting set [" << nettingSetDetails
465 << "] under regulation " << regulation);
466
467
468 const auto& regTradeData = nettingSetRegTradeData_.at(side).at(nettingSetDetails).at(regulation);
469
470 Real grossMarginCalc = 0;
471 Real grossRCCalc = 0;
472 Real presentValueCalc = 0;
473
474 // Populate results at the product class level
475 for (const auto& td : regTradeData) {
476 const IMScheduleTradeData& tradeData = td.second;
477 add(side, nettingSetDetails, regulation, tradeData.productClass, calculationCcy_, tradeData.grossMarginCalc);
478
479 // Sum up trade details to obtain netting set level values:
480 // - Gross margin, gross RC, net RC, PVs
481 grossMarginCalc += tradeData.grossMarginCalc;
482 grossRCCalc +=
483 side == SimmSide::Call ? max(0.0, tradeData.presentValueCalc) : min(0.0, tradeData.presentValueCalc);
484
485 presentValueCalc += tradeData.presentValueCalc;
486 }
487
488 // Calculate other amounts at the nettingSet-regulator level
489
490 // Net replacement cost
491 Real netRCCalc = side == SimmSide::Call ? max(0.0, presentValueCalc) : min(0.0, presentValueCalc);
492
493 // Net-to-gross ratio
494 Real netToGrossCalc = close_enough(grossRCCalc, 0.0) ? 1.0 : netRCCalc / grossRCCalc;
495
496 // Schedule IM
497 Real scheduleMarginCalc = grossMarginCalc * (0.4 + 0.6 * netToGrossCalc);
498
499 // Populate higher level results
500 imScheduleResults_.at(side)
501 .at(nettingSetDetails)
502 .at(regulation)
503 .add(ProductClass::All, calculationCcy_, grossMarginCalc, grossRCCalc, netRCCalc, netToGrossCalc,
504 scheduleMarginCalc);
505}
506
507void IMScheduleCalculator::populateFinalResults(const map<SimmSide, map<NettingSetDetails, string>>& winningRegs) {
508
509 LOG("IMScheduleCalculator: Populating final winning regulators' IM");
510
511 winningRegulations_ = winningRegs;
512
513 // Populate final IMSchedule results
514 for (const auto& sv : imScheduleResults_) {
515 const SimmSide side = sv.first;
516
517 for (const auto& nv : sv.second) {
518 const NettingSetDetails& nsd = nv.first;
519
520 const string& reg = winningRegulations(side, nsd);
521 // If no results found for winning regulator, i.e IM was calculated from SIMM only, use empty results
522 const IMScheduleResults& results =
523 nv.second.find(reg) == nv.second.end() ? IMScheduleResults(calculationCcy_) : nv.second.at(reg);
524 finalImScheduleResults_[side][nsd] = make_pair(reg, results);
525 winningRegulations_[side][nsd] = reg;
526 }
527 }
528
529 // Recombine trade data to form a single list of trade-level Schedule IM data
530 for (const auto& sv : nettingSetRegTradeData_) {
531 const SimmSide& side = sv.first;
532 for (const auto& nv : sv.second) {
533 for (const auto& rv : nv.second) {
534 const string& regulation = rv.first;
535 for (const auto& td : rv.second) {
536 // For every trade data obj, try to add this back to the finalTradeData_ map
537 const string& tradeId = td.first;
538 IMScheduleTradeData tradeData = td.second;
539
540 // We use vectors instead of trade data since the same trade ID could have multiple
541 // Schedule CRIF records, e.g. because of SIMM exemption overrides.
542 if (finalTradeData_.find(tradeId) == finalTradeData_.end())
543 finalTradeData_[tradeId] = std::vector<IMScheduleTradeData>();
544
545 if (finalTradeData_.at(tradeId).empty()) {
546 finalTradeData_.at(tradeId).push_back(tradeData);
547 } else {
548 bool foundMatch = false;
549 for (IMScheduleTradeData& tdd : finalTradeData_.at(tradeId)) {
550 // If the two trade data objs have the same data, then just combine regulations
551 if (std::tie(tdd.presentValueCcy, tdd.presentValueUsd, tdd.notionalCcy, tdd.notionalUsd) ==
552 std::tie(tradeData.presentValueCcy, tradeData.presentValueUsd, tradeData.notionalCcy,
553 tradeData.notionalUsd)) {
554 if (side == SimmSide::Call) {
555 tdd.collectRegulations = combineRegulations(tdd.collectRegulations, regulation);
556 } else {
557 tdd.postRegulations = combineRegulations(tdd.postRegulations, regulation);
558 }
559 foundMatch = true;
560 break;
561 }
562 }
563 if (!foundMatch)
564 finalTradeData_.at(tradeId).push_back(tradeData);
565 }
566 }
567 }
568 }
569 }
570}
571
574}
575
576void IMScheduleCalculator::add(const SimmSide& side, const NettingSetDetails& nsd, const string& regulation,
577 const ProductClass& pc, const string& calcCcy, const Real& grossIM, const Real& grossRC,
578 const Real& netRC, const Real& ngr, const Real& scheduleIM) {
579
580 QuantLib::Real netToGrossRatio = ngr != Null<Real>() && close_enough(ngr, 0.0) ? 0.0 : ngr;
581 imScheduleResults_[side][nsd][regulation].add(pc, calcCcy, grossIM, grossRC, netRC, netToGrossRatio, scheduleIM);
582}
583
584} // namespace analytics
585} // namespace ore
void addRecord(const CrifRecord &record, bool aggregateDifferentAmountCurrencies=false, bool sortFxVolQualifer=true)
Definition: crif.cpp:41
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::map< std::string, IMScheduleResults > > > imScheduleResults_
Containers, one for call and post, with an IMScheduleResults object for each regulation under each po...
const std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::map< std::string, IMScheduleResults > > > & imScheduleSummaryResults() const
const std::map< std::string, std::vector< IMScheduleTradeData > > & imScheduleTradeResults() const
std::map< SimmSide, std::set< NettingSetDetails > > hasCFTC_
std::map< SimmSide, std::set< NettingSetDetails > > hasSEC_
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::string > > winningRegulations_
Regulation with highest IM for each given netting set.
std::map< ore::data::NettingSetDetails, bool > collectRegsIsEmpty_
For each netting set, whether all CRIF records' collect regulations are empty.
const std::map< SimmSide, std::map< ore::data::NettingSetDetails, string > > & winningRegulations() const
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::pair< std::string, IMScheduleResults > > > finalImScheduleResults_
Containers, one for call and post, with an IMScheduleResults object for each portfolio ID.
std::map< ore::data::NettingSetDetails, bool > postRegsIsEmpty_
For each netting set, whether all CRIF records' post regulations are empty.
const std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::pair< std::string, IMScheduleResults > > > & finalImScheduleSummaryResults() const
static const IMScheduleLabel label(const ProductClass &productClass, const QuantLib::Real &maturity)
std::map< SimmSide, std::map< ore::data::NettingSetDetails, std::map< std::string, std::map< std::string, IMScheduleTradeData > > > > nettingSetRegTradeData_
Container for trade data, taking into account regulations applicable to each netting set.
QuantLib::ext::shared_ptr< ore::data::Market > market_
Market data for FX rates to use for converting amounts to USD.
std::map< std::string, std::vector< IMScheduleTradeData > > finalTradeData_
static const std::string labelString(const IMScheduleLabel &label)
std::string calculationCcy_
The SIMM calculation currency i.e. the currency of the SIMM results.
IMScheduleCalculator(const Crif &crif, const std::string &calculationCcy="USD", 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 > >())
Construct the IMScheduleCalculator from a container of netted CRIF records.
ore::analytics::Crif crif_
The net sensitivities used in the calculation.
void collectTradeData(const CrifRecord &cr, const bool enforceIMRegulations)
Collect trade data as defined by the CRIF records.
void add(const SimmSide &side, const ore::data::NettingSetDetails &nsd, const std::string &regulation, const CrifRecord::ProductClass &pc, const std::string &ccy, const QuantLib::Real &grossIM, const QuantLib::Real &grossRC=QuantLib::Null< QuantLib::Real >(), const QuantLib::Real &netRC=QuantLib::Null< QuantLib::Real >(), const QuantLib::Real &ngr=QuantLib::Null< QuantLib::Real >(), const QuantLib::Real &scheduleIM=QuantLib::Null< QuantLib::Real >())
Add a margin result to either call or post results container depending on the SimmSide parameter.
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.
QuantLib::Real multiplier(const IMScheduleLabel &label)
void populateResults(const ore::data::NettingSetDetails &nsd, const string &regulation, const SimmSide &side)
IMScheduleResult get(const CrifRecord::ProductClass &pc) const
Convert Schedule IM amounts to a different currency.
SimmSide
Enum indicating the relevant side of the SIMM calculation.
Struct for holding a CRIF record.
Date parseDate(const string &s)
bool parseBool(const string &s)
bool checkCurrency(const string &code)
Class for calculating SIMM.
Class for holding IMSchedule results.
#define LOG(text)
#define DLOG(text)
Time maturity
RandomVariable max(RandomVariable x, const RandomVariable &y)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
RandomVariable min(RandomVariable x, const RandomVariable &y)
string combineRegulations(const string &regs1, const string &regs2)
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.
std::string to_string(const LocationInfo &l)
NettingSetDetails nettingSetDetails
Definition: crifrecord.hpp:156
Class for structured analytics warnings.
supporting utilities