/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.credit.isdastandardmodel;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZonedDateTime;
import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.DateTimeFormatterBuilder;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantDateCreditCurve;
import com.opengamma.util.ArgumentChecker;
import au.com.bytecode.opencsv.CSVReader;
/**
* 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 CSVReader _csvReader;
private String[] _headers;
// header fields we expect in the file (lowercased when loaded)
private static final String TODAY_HEADER = "today".toLowerCase();
@SuppressWarnings("unused")
private static final String CURVE_INSTRUMENT_START_DATE = "curve instrument start date".toLowerCase();
private static final String START_DATE_HEADER = "start date".toLowerCase();
private static final String END_DATE_HEADER = "end date".toLowerCase();
private static final String SPREAD_HEADER = "spread".toLowerCase();
@SuppressWarnings("unused")
private static final String CLEAN_PRICE_HEADER = "clean price".toLowerCase();
@SuppressWarnings("unused")
private static final String CLEAN_PRICE_NOACC_HEADER = "clean price (no acc on default)".toLowerCase();
@SuppressWarnings("unused")
private static final String DIRTY_PRICE_NOACC_HEADER = "dirty price (no acc on default)".toLowerCase();
private static final String PREMIUM_LEG_HEADER = "premium leg".toLowerCase();
private static final String PROTECTION_LEG_HEADER = "protection leg".toLowerCase();
private static final String DEFAULT_ACC_HEADER = "default acc".toLowerCase();
private static final String ACC_PREMIUM_HEADER = "accrued premium".toLowerCase();
private static final String ACC_DAYS_HEADER = "accrued days".toLowerCase();
private static final DateTimeFormatter DATE_TIME_PARSER = new DateTimeFormatterBuilder().appendPattern("dd-MMM-yy").toFormatter();
// 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) {
ArgumentChecker.notEmpty(sheetName, "filename");
// Open file
final String sheetFilePath = SHEET_LOCATION + sheetName;
final InputStream is = ISDAModelDatasetsSheetReader.class.getClassLoader().getResourceAsStream(sheetFilePath);
if (is == null) {
throw new OpenGammaRuntimeException(sheetFilePath + ": does not exist");
}
// Set up CSV reader
_csvReader = new CSVReader(new InputStreamReader(is));
// Set columns
_headers = readHeaderRow();
Map<String, String> row;
while ((row = loadNextRow()) != null) {
ISDA_Results temp = getResult(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 OpenGammaRuntimeException(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 OpenGammaRuntimeException(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].toString(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.getDayCountFraction(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
String[] rawRow;
try {
rawRow = _csvReader.readNext();
} catch (IOException ex) {
throw new OpenGammaRuntimeException("Error reading CSV file header row: " + ex.getMessage());
}
final List<LocalDate> parSpreadDates = new ArrayList<>();
// Normalise read-in headers (to lower case) and set as columns
String[] columns = new String[rawRow.length];
for (int i = 0; i < rawRow.length; i++) {
columns[i] = rawRow[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(); // 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);
}
ArgumentChecker.notEmpty(_parSpreadDates, "par spread dates");
ArgumentChecker.notEmpty(_curveTenors, "curve tenors");
return columns;
}
public Map<String, String> loadNextRow() {
// Read in next row
String[] rawRow;
try {
rawRow = _csvReader.readNext();
} catch (IOException ex) {
throw new OpenGammaRuntimeException("Error reading CSV file data row: " + ex.getMessage());
}
// Return null if EOF
if (rawRow == null) {
return null;
}
// Map read-in row onto expected columns
Map<String, String> result = new HashMap<>();
for (int i = 0; i < _headers.length; i++) {
if (i >= rawRow.length) {
break;
}
if (rawRow[i] != null && rawRow[i].trim().length() > 0) {
result.put(_headers[i], rawRow[i]);
}
}
return result;
}
}