/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.credit.isda; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import com.google.common.collect.ImmutableList; import com.google.common.io.Resources; import com.opengamma.strata.collect.ArgChecker; import com.opengamma.strata.collect.io.CsvFile; import com.opengamma.strata.collect.io.CsvRow; /** * Simple class to read in a csv file with ISDA inputs load them into a test harness * * Could extend in a more generic way but left simple for now. */ public class IsdaModelDatasetsSheetReader extends IsdaModelDatasets { private static final String SHEET_LOCATION = "isda_comparison_sheets/"; private final List<ISDA_Results> results = new ArrayList<>(100); // ~100 rows nominally private CsvFile csvFile; private String[] headers; // header fields we expect in the file (lowercased when loaded) private static final String TODAY_HEADER = "today"; @SuppressWarnings("unused") private static final String CURVE_INSTRUMENT_START_DATE = "curve instrument start date"; private static final String START_DATE_HEADER = "start date"; private static final String END_DATE_HEADER = "end date"; private static final String SPREAD_HEADER = "spread"; @SuppressWarnings("unused") private static final String CLEAN_PRICE_HEADER = "clean price"; @SuppressWarnings("unused") private static final String CLEAN_PRICE_NOACC_HEADER = "clean price (no acc on default)"; @SuppressWarnings("unused") private static final String DIRTY_PRICE_NOACC_HEADER = "dirty price (no acc on default)"; private static final String PREMIUM_LEG_HEADER = "premium leg"; private static final String PROTECTION_LEG_HEADER = "protection leg"; private static final String DEFAULT_ACC_HEADER = "default acc"; private static final String ACC_PREMIUM_HEADER = "accrued premium"; private static final String ACC_DAYS_HEADER = "accrued days"; private static final DateTimeFormatter DATE_TIME_PARSER = DateTimeFormatter.ofPattern("dd-MMM-uu", Locale.ENGLISH); // component parts of the resultant ISDA_Results instances private LocalDate[] _parSpreadDates; // assume in ascending order private ZonedDateTime[] _curveTenors; /** * Load specified sheet. * * @param sheetName the sheet name * @param recoveryRate the recovery rate * @return at set of ISDA results */ public static ISDA_Results[] loadSheet(final String sheetName, final double recoveryRate) { return new IsdaModelDatasetsSheetReader(sheetName, recoveryRate).getResults(); } /** * Load specified sheet. * * @param sheetName the sheet name * @param recoveryRate the recovery rate */ public IsdaModelDatasetsSheetReader(final String sheetName, final double recoveryRate) { ArgChecker.notEmpty(sheetName, "filename"); // Set up CSV reader String sheetFilePath = SHEET_LOCATION + sheetName; URL resource = IsdaModelDatasetsSheetReader.class.getClassLoader().getResource(sheetFilePath); if (resource == null) { throw new IllegalArgumentException(sheetFilePath + ": does not exist"); } csvFile = CsvFile.of(Resources.asCharSource(resource, StandardCharsets.UTF_8), true); // Set columns headers = readHeaderRow(); for (CsvRow row : csvFile.rows()) { ISDA_Results temp = getResult(parseRow(row)); temp.recoveryRate = recoveryRate; results.add(temp); } } private ISDA_Results getResult(Map<String, String> fields) { final ISDA_Results result = new ISDA_Results(); result.today = getLocalDate(TODAY_HEADER, fields); result.startDate = getLocalDate(START_DATE_HEADER, fields); result.endDate = getLocalDate(END_DATE_HEADER, fields); result.protectionLeg = getDouble(PROTECTION_LEG_HEADER, fields); result.premiumLeg = getDouble(PREMIUM_LEG_HEADER, fields); result.defaultAcc = getDouble(DEFAULT_ACC_HEADER, fields); result.accruedPremium = getDouble(ACC_PREMIUM_HEADER, fields); result.accruedDays = new Double(getDouble(ACC_DAYS_HEADER, fields)).intValue(); result.fracSpread = getDouble(SPREAD_HEADER, fields) / 10000; result.creditCurve = getCreditCurve(fields, result.today); return result; } private double getDouble(final String field, final Map<String, String> fields) { if (fields.containsKey(field)) { return Double.valueOf(fields.get(field)); } throw new IllegalArgumentException(field + " not present in sheet row, got " + fields); } private LocalDate getLocalDate(final String field, final Map<String, String> fields) { if (fields.containsKey(field)) { return LocalDate.parse(fields.get(field), DATE_TIME_PARSER); } throw new IllegalArgumentException(field + " not present in sheet row, got " + fields); } private IsdaCompliantDateCreditCurve getCreditCurve(final Map<String, String> fields, final LocalDate today) { // load the curve dates from the inputs final int nCurvePoints = _parSpreadDates.length; final double[] negLogP = new double[nCurvePoints]; for (int i = 0; i < nCurvePoints; i++) { negLogP[i] = getDouble(_parSpreadDates[i].format(DATE_TIME_PARSER), fields); } final double[] t = new double[nCurvePoints]; final double[] r = new double[nCurvePoints]; for (int j = 0; j < nCurvePoints; j++) { t[j] = ACT365.yearFraction(today, _parSpreadDates[j]); r[j] = negLogP[j] / t[j]; } return new IsdaCompliantDateCreditCurve(today, _parSpreadDates, r, ACT365); } public ISDA_Results[] getResults() { return results.toArray(new ISDA_Results[results.size()]); } private String[] readHeaderRow() { // Read in the header row ImmutableList<String> rawRow = csvFile.headers(); final List<LocalDate> parSpreadDates = new ArrayList<>(); // Normalise read-in headers (to lower case) and set as columns String[] columns = new String[rawRow.size()]; for (int i = 0; i < rawRow.size(); i++) { columns[i] = rawRow.get(i).trim(); // if a date add to list of spread dates try { final LocalDate date = LocalDate.parse(columns[i], DATE_TIME_PARSER); parSpreadDates.add(date); continue; } catch (Exception ex) { columns[i] = columns[i].toLowerCase(Locale.ENGLISH); // lowercase non dates } } _parSpreadDates = parSpreadDates.toArray(new LocalDate[parSpreadDates.size()]); _curveTenors = new ZonedDateTime[_parSpreadDates.length]; for (int j = 0; j < _parSpreadDates.length; j++) { _curveTenors[j] = ZonedDateTime.of(_parSpreadDates[j], LOCAL_TIME, TIME_ZONE); } ArgChecker.notEmpty(_parSpreadDates, "par spread dates"); ArgChecker.notEmpty(_curveTenors, "curve tenors"); return columns; } // map row onto expected columns private Map<String, String> parseRow(CsvRow rawRow) { Map<String, String> result = new HashMap<>(); for (int i = 0; i < headers.length; i++) { if (i >= rawRow.fieldCount()) { break; } if (rawRow.field(i) != null && rawRow.field(i).trim().length() > 0) { result.put(headers[i], rawRow.field(i)); } } return result; } }