Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Member Functions | Protected Member Functions | Protected Attributes | List of all members
StringStreamCrifLoader Class Referenceabstract

#include <orea/simm/crifloader.hpp>

+ Inheritance diagram for StringStreamCrifLoader:
+ Collaboration diagram for StringStreamCrifLoader:

Public Member Functions

 StringStreamCrifLoader (const QuantLib::ext::shared_ptr< SimmConfiguration > &configuration, const std::vector< std::set< std::string > > &additionalHeaders={}, bool updateMapper=false, bool aggregateTrades=true, char eol='\n', char delim='\t', char quoteChar='\0', char escapeChar='\\', const std::string &nullString="#N/A")
 
- Public Member Functions inherited from CrifLoader
 CrifLoader (const QuantLib::ext::shared_ptr< SimmConfiguration > &configuration, const std::vector< std::set< std::string > > &additionalHeaders={}, bool updateMapper=false, bool aggregateTrades=true)
 
virtual ~CrifLoader ()
 
virtual Crif loadCrif ()
 
const QuantLib::ext::shared_ptr< SimmConfiguration > & simmConfiguration ()
 SIMM configuration getter. More...
 

Protected Member Functions

Crif loadCrifImpl () override
 
Crif loadFromStream (std::stringstream &&stream)
 Core CRIF loader from generic istream. More...
 
virtual std::stringstream stream () const =0
 
void processHeader (const std::vector< std::string > &headers)
 Process the elements of a header line of a CRIF file. More...
 
bool process (const std::vector< std::string > &entries, QuantLib::Size maxIndex, QuantLib::Size currentLine, Crif &result)
 
- Protected Member Functions inherited from CrifLoader
virtual Crif loadCrifImpl ()=0
 
void addRecordToCrif (Crif &crif, CrifRecord &&recordToAdd) const
 
void validateSimmRecord (const CrifRecord &cr) const
 Check if the record is a valid Simm Crif Record. More...
 
void currencyOverrides (CrifRecord &crifRecord) const
 Override currency codes. More...
 
void updateMapping (const CrifRecord &cr) const
 update bucket mappings More...
 

Protected Attributes

std::map< QuantLib::Size, QuantLib::Size > columnIndex_
 
std::map< QuantLib::Size, std::set< std::string > > additionalHeadersIndexMap_
 
char eol_
 
char delim_
 
char quoteChar_
 
char escapeChar_
 
std::string nullString_
 
- Protected Attributes inherited from CrifLoader
QuantLib::ext::shared_ptr< SimmConfigurationconfiguration_
 Simm configuration that is used during loading of CRIF records. More...
 
std::vector< std::set< std::string > > additionalHeaders_
 Defines accepted column headers, beyond required and optional headers, see crifloader.cpp. More...
 
bool updateMapper_
 
bool aggregateTrades_
 

Additional Inherited Members

- Static Protected Attributes inherited from CrifLoader
static std::map< QuantLib::Size, std::set< std::string > > requiredHeaders
 Map giving required CRIF file headers and their allowable alternatives. More...
 
static std::map< QuantLib::Size, std::set< std::string > > optionalHeaders
 Map giving optional CRIF file headers and their allowable alternatives. More...
 

Detailed Description

Definition at line 101 of file crifloader.hpp.

Constructor & Destructor Documentation

◆ StringStreamCrifLoader()

StringStreamCrifLoader ( const QuantLib::ext::shared_ptr< SimmConfiguration > &  configuration,
const std::vector< std::set< std::string > > &  additionalHeaders = {},
bool  updateMapper = false,
bool  aggregateTrades = true,
char  eol = '\n',
char  delim = '\t',
char  quoteChar = '\0',
char  escapeChar = '\\',
const std::string &  nullString = "#N/A" 
)

Definition at line 197 of file crifloader.cpp.

200 : CrifLoader(configuration, additionalHeaders, updateMapper, aggregateTrades), eol_(eol), delim_(delim),
201 quoteChar_(quoteChar), escapeChar_(escapeChar), nullString_(nullString) {
202
203 size_t maxIndexRequired = *boost::max_element(requiredHeaders | boost::adaptors::map_keys);
204 size_t maxIndexOptional = *boost::max_element(optionalHeaders | boost::adaptors::map_keys);
205 size_t maxIndex = std::max(maxIndexRequired, maxIndexOptional);
206
207 size_t i = 1;
208 for (const auto& addHeader : additionalHeaders_) {
209 additionalHeadersIndexMap_[maxIndex + i] = addHeader;
210 i++;
211 }
212
213}
static std::map< QuantLib::Size, std::set< std::string > > requiredHeaders
Map giving required CRIF file headers and their allowable alternatives.
Definition: crifloader.hpp:95
static std::map< QuantLib::Size, std::set< std::string > > optionalHeaders
Map giving optional CRIF file headers and their allowable alternatives.
Definition: crifloader.hpp:98
CrifLoader(const QuantLib::ext::shared_ptr< SimmConfiguration > &configuration, const std::vector< std::set< std::string > > &additionalHeaders={}, bool updateMapper=false, bool aggregateTrades=true)
Definition: crifloader.hpp:46
std::vector< std::set< std::string > > additionalHeaders_
Defines accepted column headers, beyond required and optional headers, see crifloader....
Definition: crifloader.hpp:82
std::map< QuantLib::Size, std::set< std::string > > additionalHeadersIndexMap_
Definition: crifloader.hpp:122

Member Function Documentation

◆ loadCrifImpl()

Crif loadCrifImpl ( )
overrideprotectedvirtual

Implements CrifLoader.

Definition at line 109 of file crifloader.hpp.

109{ return loadFromStream(stream()); }
virtual std::stringstream stream() const =0
Crif loadFromStream(std::stringstream &&stream)
Core CRIF loader from generic istream.
Definition: crifloader.cpp:233
+ Here is the call graph for this function:

◆ loadFromStream()

Crif loadFromStream ( std::stringstream &&  stream)
protected

Core CRIF loader from generic istream.

Definition at line 233 of file crifloader.cpp.

233 {
234 string line;
235 vector<string> entries;
236 bool headerProcessed = false;
237 Size emptyLines = 0;
238 Size validLines = 0;
239 Size invalidLines = 0;
240 Size maxIndex = 0;
241 Size currentLine = 0;
242 Crif result;
243 while (getline(stream, line, eol_)) {
244
245 // Keep track of current line number for messages
246 ++currentLine;
247
248 // Trim leading and trailing space
249 boost::trim(line);
250
251 // Skip empty lines
252 if (line.empty()) {
253 ++emptyLines;
254 continue;
255 }
256
257 // Break line up in to its elements.
259
260 if (headerProcessed) {
261 // Process a regular line of the CRIF file
262 if (process(entries, maxIndex, currentLine, result)) {
263 ++validLines;
264 } else {
265 ++invalidLines;
266 }
267 } else {
268 // Process the header line of the CRIF file
269 processHeader(entries);
270 headerProcessed = true;
271 auto maxPair = max_element(
272 columnIndex_.begin(), columnIndex_.end(),
273 [](const pair<Size, Size>& p1, const pair<Size, Size>& p2) { return p1.second < p2.second; });
274 maxIndex = maxPair->second;
275 }
276 }
277
278 LOG("Out of " << currentLine << " lines, there were " << validLines << " valid lines, " << invalidLines
279 << " invalid lines and " << emptyLines << " empty lines.");
280 return result;
281}
std::map< QuantLib::Size, QuantLib::Size > columnIndex_
Definition: crifloader.hpp:119
bool process(const std::vector< std::string > &entries, QuantLib::Size maxIndex, QuantLib::Size currentLine, Crif &result)
Definition: crifloader.cpp:336
void processHeader(const std::vector< std::string > &headers)
Process the elements of a header line of a CRIF file.
Definition: crifloader.cpp:284
#define LOG(text)
std::vector< string > parseListOfValues(string s, const char escape, const char delim, const char quote)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ stream()

virtual std::stringstream stream ( ) const
protectedpure virtual

Implemented in CsvFileCrifLoader, and CsvBufferCrifLoader.

+ Here is the caller graph for this function:

◆ processHeader()

void processHeader ( const std::vector< std::string > &  headers)
protected

Process the elements of a header line of a CRIF file.

Definition at line 284 of file crifloader.cpp.

284 {
285 columnIndex_.clear();
286
287 // Get mapping for all required headers in to column index in the file
288 string header;
289 for (const auto& kv : requiredHeaders) {
290 for (Size i = 0; i < headers.size(); ++i) {
291 header = boost::to_lower_copy(headers[i]);
292 if (kv.second.count(header) > 0) {
293 columnIndex_[kv.first] = i;
294 }
295 }
296 // Some headers are allowed to be missing (under certain circumstances)
297 // trade_id, portfolioid and productclass arent required for frtb crif
298 if (kv.first == 0 || kv.first == 1 || kv.first == 2) {
299 // Portfolio ID header is allowed to be missing
300 if (columnIndex_.count(kv.first) == 0) {
301 WLOG("Did not find a header for portfolioid in the CRIF file so using a default value");
302 }
303 } else if (kv.first == 10) {
304 // Allow either amount_usd missing or amount and amount_currency, but not all three.
305 // For SIMM, we ultimately use amount_usd, but if missing, we use amount and amount_currency
306 // and let the SimmCalculator handle the conversion to amount_usd
307 if (columnIndex_.count(10) == 0)
308 QL_REQUIRE(columnIndex_.count(8) > 0 && columnIndex_.count(9) > 0,
309 "Must provide either amount and amount_currency, or amount_usd");
310 } else {
311 // All other headers should be there
312 QL_REQUIRE(columnIndex_.count(kv.first) > 0,
313 "Could not find a header in the CRIF file for " << *kv.second.begin());
314 }
315 }
316
317 for (const auto& kv : optionalHeaders) {
318 for (Size i = 0; i < headers.size(); ++i) {
319 header = boost::to_lower_copy(headers[i]);
320 if (kv.second.count(header) > 0) {
321 columnIndex_[kv.first] = i;
322 }
323 }
324 }
325
326 for (const auto& kv: additionalHeadersIndexMap_) {
327 for (Size columnPos = 0; columnPos < headers.size(); ++columnPos) {
328 header = boost::to_lower_copy(headers[columnPos]);
329 if (kv.second.count(header) > 0) {
330 columnIndex_[kv.first] = columnPos;
331 }
332 }
333 }
334}
#define WLOG(text)
+ Here is the caller graph for this function:

◆ process()

bool process ( const std::vector< std::string > &  entries,
QuantLib::Size  maxIndex,
QuantLib::Size  currentLine,
Crif result 
)
protected

Process a line of a CRIF file and return true if valid line or false if an invalid line

Definition at line 336 of file crifloader.cpp.

336 {
337 CrifRecord cr;
338 // Return early if there are not enough entries in the line
339 if (entries.size() <= maxIndex) {
340 WLOG("Line number: " << currentLine << ". Expected at least " << maxIndex + 1 << " entries but got only "
341 << entries.size());
342 return false;
343 }
344
345 // Try to create and add a CRIF record
346 // There could still be issues here so we surround with try..catch to allow processing to continue
347 auto loadOptionalString = [&entries, this](int column) {
348 return columnIndex_.count(column) == 0 ? "" : entries[columnIndex_[column]];
349 };
350 auto loadOptionalReal = [&entries, this](int column) -> QuantLib::Real{
351 if (columnIndex_.count(column) == 0) {
352 return QuantLib::Null<QuantLib::Real>();
353 } else{
354 const std::string& value = entries[columnIndex_[column]];
355
356 return value.empty() || value == nullString_ ? QuantLib::Null<QuantLib::Real>()
357 : parseReal(value);
358 }
359 };
360
361 string tradeId, tradeType, imModel;
362 try {
363 tradeId = loadOptionalString(0);
364 tradeType = loadOptionalString(15);
365 imModel = loadOptionalString(16);
366
367 cr.tradeId = tradeId;
368 cr.tradeType = tradeType;
369 cr.imModel = imModel;
370 cr.portfolioId = columnIndex_.count(1) == 0 ? "DummyPortfolio" : entries[columnIndex_.at(1)];
371 cr.productClass = parseProductClass(loadOptionalString(2));
372 cr.riskType = parseRiskType(entries[columnIndex_.at(3)]);
373
374 // Qualifier - There are many other possible qualifier values, but we only do case-insensitive checks
375 // for those with standardised values, i.e. currencies or ccy pairs
376 cr.qualifier = entries[columnIndex_.at(4)];
377 if ((cr.riskType == RiskType::IRCurve || cr.riskType == RiskType::IRVol || cr.riskType == RiskType::FX) &&
378 cr.qualifier.size() == 3) {
379 string ccyUpper = boost::to_upper_copy(cr.qualifier);
380
381 // If ccy is already valid, do nothing. Otherwise, replace with all uppercase equivalent.
382 // FIXME: Minor currencies will fail to get spotted here, though it is not likely that we will have
383 // a qualifier in a minor ccy?
384 if (!checkCurrency(cr.qualifier) && checkCurrency(ccyUpper))
385 cr.qualifier = ccyUpper;
386 } else if (cr.riskType == RiskType::FXVol && (cr.qualifier.size() == 6 || cr.qualifier.size() == 7)) {
387
388 // Remove delimiters between the two currencies
389 const string ccyPairDelimiters = "/.,-_|;: ";
390 auto ccyPair = ore::data::parseCurrencyPair(boost::to_upper_copy(cr.qualifier), ccyPairDelimiters);
391
392 // Convert to uppercase
393 string ccy1Upper = ccyPair.first.code();
394 string ccy2Upper = ccyPair.second.code();
395
396 cr.qualifier = ccy1Upper + ccy2Upper;
397 }
398
399 // Bucket - Hardcoded "Residual" for case-insensitive check since this is currently the only non-numeric value
400 cr.bucket = entries[columnIndex_.at(5)];
401 if (boost::to_lower_copy(cr.bucket) == "residual")
402 cr.bucket = "Residual";
403
404 // Label1
405 cr.label1 = entries[columnIndex_.at(6)];
406 if (configuration_->isValidRiskType(cr.riskType)) {
407 for (const string& l : configuration_->labels1(cr.riskType)) {
408 if (boost::to_lower_copy(cr.label1) == boost::to_lower_copy(l))
409 cr.label1 = l;
410 }
411 }
412 // Label2
413 cr.label2 = entries[columnIndex_.at(7)];
414 if (configuration_->isValidRiskType(cr.riskType)) {
415 for (const string& l : configuration_->labels2(cr.riskType)) {
416 if (boost::to_lower_copy(cr.label2) == boost::to_lower_copy(l))
417 cr.label2 = l;
418 }
419 }
420
421 // We populate these 'required' values using loadOptional*, but they will have been validated already in processHeader,
422 // and missing amountUsd (but with valid amount and amountCurrency) values populated later on in the analytics
423
424 cr.amountCurrency = loadOptionalString(8);
425 string amountCcyUpper = boost::to_upper_copy(cr.amountCurrency);
426 if (!amountCcyUpper.empty() && !checkCurrency(cr.amountCurrency) && checkCurrency(amountCcyUpper))
427 cr.amountCurrency = amountCcyUpper;
428
429 cr.amount = loadOptionalReal(9);
430 cr.amountUsd = loadOptionalReal(10);
431
432 // Populate netting set details
433 cr.agreementType = loadOptionalString(11);
434 cr.callType = loadOptionalString(12);
435 cr.initialMarginType = loadOptionalString(13);
436 cr.legalEntityId = loadOptionalString(14);
437 cr.nettingSetDetails = NettingSetDetails(cr.portfolioId, cr. agreementType, cr.callType, cr.initialMarginType,
438 cr.legalEntityId);
439 cr.postRegulations = loadOptionalString(17);
440 cr.collectRegulations = loadOptionalString(18);
441 cr.endDate = loadOptionalString(19);
442 cr.label3 = loadOptionalString(20);
443 cr.creditQuality = loadOptionalString(21);
444 cr.longShortInd = loadOptionalString(22);
445 cr.coveredBondInd = loadOptionalString(23);
446 cr.trancheThickness = loadOptionalString(24);
447 cr.bb_rw = loadOptionalString(25);
448
449 // Check the IM model
450 try {
451 cr.imModel = to_string(parseIMModel(cr.imModel));
452 } catch (...) {
453 // If we cannot convert to a valid im_model, then it was either provided blank
454 // or is simply not a valid value
455 }
456
457 // Store additional data that matches the defined additional headers in the additional fields map
458 for (auto& additionalField : additionalHeadersIndexMap_) {
459 std::string value = loadOptionalString(additionalField.first);
460 if (!value.empty())
461 cr.additionalFields[*additionalField.second.begin()] = value;
462 }
463
464 // Add the CRIF record to the net records
465 addRecordToCrif(result, std::move(cr));
466 } catch (const exception& e) {
467 ore::data::StructuredTradeErrorMessage(tradeId, tradeType, "CRIF loading",
468 "Line number: " + to_string(currentLine) +
469 ". Error processing CRIF line, so skipping it. Error: " + to_string(e.what()))
470 .log();
471 return false;
472 }
473
474 return true;
475}
QuantLib::ext::shared_ptr< SimmConfiguration > configuration_
Simm configuration that is used during loading of CRIF records.
Definition: crifloader.hpp:79
void addRecordToCrif(Crif &crif, CrifRecord &&recordToAdd) const
Definition: crifloader.cpp:95
SafeStack< ValueType > value
pair< Currency, Currency > parseCurrencyPair(const string &s, const string &delimiters)
Real parseReal(const string &s)
bool checkCurrency(const string &code)
SimmConfiguration::IMModel parseIMModel(const string &model)
CrifRecord::RiskType parseRiskType(const string &rt)
Definition: crifrecord.cpp:117
CrifRecord::ProductClass parseProductClass(const string &pc)
Definition: crifrecord.cpp:127
std::string to_string(const LocationInfo &l)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ columnIndex_

std::map<QuantLib::Size, QuantLib::Size> columnIndex_
protected

Internal map from known index of CRIF record member to file column For example, give trade ID an index of 0 and find the column index of trade ID in the CRIF file e.g. n. The map entry would be [0, n]

Definition at line 119 of file crifloader.hpp.

◆ additionalHeadersIndexMap_

std::map<QuantLib::Size, std::set<std::string> > additionalHeadersIndexMap_
protected

Definition at line 122 of file crifloader.hpp.

◆ eol_

char eol_
protected

Definition at line 131 of file crifloader.hpp.

◆ delim_

char delim_
protected

Definition at line 132 of file crifloader.hpp.

◆ quoteChar_

char quoteChar_
protected

Definition at line 133 of file crifloader.hpp.

◆ escapeChar_

char escapeChar_
protected

Definition at line 134 of file crifloader.hpp.

◆ nullString_

std::string nullString_
protected

Definition at line 135 of file crifloader.hpp.