/// <summary>Opens a CRIF file and reads its content.</summary> /// <param name="filePath">The <see cref="T:System.String"/> representing the file to open for reading.</param> /// <returns>An <see cref="System.Collections.Generic.ICollection{T}"/> of <see cref="T:InitialMargin.Core.DataEntity"/> objects defined in the CRIF file.</returns> /// <exception cref="T:System.ArgumentException">Thrown when <paramref name="filePath">filePath</paramref> is invalid or does not refer to a CSV file.</exception> /// <exception cref="T:System.IO.FileNotFoundException">Thrown when <paramref name="filePath">filePath</paramref> could not be found.</exception> /// <exception cref="T:System.IO.InvalidDataException">Thrown when the CRIF file contains invalid or malformed data.</exception> public static ICollection <DataEntity> Read(String filePath) { List <String[]> fieldsMatrix = CsvParser.Parse(filePath, Encoding.UTF8, '\t', false); if (fieldsMatrix.Count == 0) { throw new InvalidDataException($"[{filePath}] The CRIF file is empty."); } String[] properties = fieldsMatrix.ElementAt(0); fieldsMatrix = fieldsMatrix.Skip(1).ToList(); if (properties.Any(String.IsNullOrWhiteSpace)) { throw new InvalidDataException($"[{filePath}] The CRIF file contains empty column headers."); } if (s_Properties.Intersect(properties).Count() != s_Properties.Length) { throw new InvalidDataException($"[{filePath}] The CRIF file does not contain all the required column headers: {String.Join(", ", s_Properties)}."); } Dictionary <String, Int32> propertiesMapping = new Dictionary <String, Int32>(s_Properties.Length); foreach (String property in s_Properties) { propertiesMapping.Add(property, Array.IndexOf(properties, property)); } List <DataEntity> entities = new List <DataEntity>(); foreach (var tuple in fieldsMatrix.Select((x, i) => new { Index = i, Value = x })) { Int32 index = tuple.Index + 2; String[] values = tuple.Value; if (values.Length != properties.Length) { throw new InvalidDataException($"[{filePath}, Line {index}] The CRIF file contains an entry whose number of columns doesn't match the number of column headers."); } EntryObject o = ToEntryObject(values, propertiesMapping); if ((o.InitialMarginModel != "Schedule") && (o.InitialMarginModel != "SIMM")) { throw new InvalidDataException($"[{filePath}, Line {index}] The CRIF file contains an entry with an invalid IMModel property."); } DataEntity entity = null; try { switch (o.RiskType) { case "Notional": { if (o.InitialMarginModel == "SIMM") { entity = ReadAddOnNotional(o); } else { entity = ReadNotional(o); } break; } case "Param_AddOnFixedAmount": entity = ReadAddOnFixedAmount(o); break; case "Param_AddOnNotionalFactor": entity = ReadAddOnNotionalFactor(o); break; case "Param_ProductClassMultiplier": entity = ReadProductMultiplier(o); break; case "PV": entity = ReadPresentValue(o); break; case "Risk_BaseCorr": entity = ReadSensitivityBaseCorrelation(o); break; case "Risk_Commodity": case "Risk_CommodityVol": entity = ParseSensitivityCommodity(o); break; case "Risk_CreditNonQ": case "Risk_CreditVolNonQ": entity = ParseSensitivityCreditNonQualifying(o); break; case "Risk_CreditQ": case "Risk_CreditVol": entity = ParseSensitivityCreditQualifying(o); break; case "Risk_Equity": case "Risk_EquityVol": entity = ReadSensitivityEquity(o); break; case "Risk_FX": case "Risk_FXVol": entity = ReadSensitivityFx(o); break; case "Risk_Inflation": case "Risk_InflationVol": entity = ReadSensitivityInflation(o); break; case "Risk_IRCurve": case "Risk_IRVol": entity = ReadSensitivityInterestRate(o); break; case "Risk_XCcyBasis": entity = ReadSensitivityCrossCurrencyBasis(o); break; } } catch (InvalidDataException e) { throw new InvalidDataException($"[{filePath}, Line {index}] {e.Message}"); } if (entity == null) { throw new InvalidDataException($"[{filePath}, Line {index}] The CRIF file contains an entry with an invalid RiskType property."); } entities.Add(entity); } return(entities); }