/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.solutions.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; import com.google.common.collect.Lists; import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.time.Tenor; import au.com.bytecode.opencsv.CSVReader; /** * Utility class for curves */ public final class CurveUtils { private CurveUtils() { /* private constructor */ } private static final Logger s_logger = LoggerFactory.getLogger(CurveUtils.class); /** * Reads and parses curve data into in memory raw representation * The format of the file is simple csv with a header row: * Name,Tenor,Value * USD-Federal Funds,1D,0.05918 * USD-Federal Funds,1W,0.06263 * USD-Federal Funds,2M,0.06415 * USD-Federal Funds,1Y,0.07234 * * @param file the data to be parsed * @return the parsed curve data by curve name */ public static Map<String, CurveRawData> parseCurves(String file) throws IOException { s_logger.info("Creating curves from {}.", file); Map<String, CurveRawData> curveData = new HashMap<>(); Reader curveReader = new BufferedReader( new InputStreamReader( new ClassPathResource(file).getInputStream() ) ); try { CSVReader csvReader = new CSVReader(curveReader); String[] line; csvReader.readNext(); // skip headers while ((line = csvReader.readNext()) != null) { String name = line[0]; String tenor = line[1]; if (tenor.endsWith("W")) { int weeks = Integer.parseInt(tenor.substring(0, tenor.length() - 1)); tenor = (weeks * 7) + "D"; } else if (tenor.endsWith("M")) { int months = Integer.parseInt(tenor.substring(0, tenor.length() - 1)); tenor = Math.round(months / 12d * 365) + "D"; } else if (tenor.endsWith("Y")) { int years = Integer.parseInt(tenor.substring(0, tenor.length() - 1)); tenor = (years * 365) + "D"; } Tenor tenorValue; try { tenorValue = Tenor.parse("P" + tenor); } catch (NumberFormatException e) { s_logger.error("Invalid tenor {} for {} in file {}. Input tenor values should contain and end with one of the following D, W, M and Y", tenor, name, file); continue; } String quote = line[2]; double quoteValue; try { quoteValue = Double.parseDouble(quote); } catch (NumberFormatException e) { s_logger.error("Invalid quote {} for {} at {}", quote, name, tenor); continue; } if (!curveData.containsKey(name)) { curveData.put(name, new CurveRawData()); } curveData.get(name).add(tenorValue, quoteValue); } } catch (IOException e) { s_logger.error("Failed to parse curve data ", e); } return curveData; } /** * Create instance of a {@link ForwardCurve} * @param data the {@link CurveRawData} to create the curve from * @return a ForwardCurve */ public static ForwardCurve createForwardCurve(CurveRawData data) { List<Tenor> tenors = Lists.newArrayList(data.getCurvePoint()); double[] maturities = new double[tenors.size()]; double[] prices = new double[tenors.size()]; for (int i = 0; i < tenors.size(); i++) { Tenor tenor = tenors.get(i); maturities[i] = tenor.getPeriod().getDays() / 365d; prices[i] = data.getValue(tenor); } Interpolator1D interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator( Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); return new ForwardCurve(InterpolatedDoublesCurve.from(maturities, prices, interpolator)); } /** * Create instance of {@link YieldAndDiscountCurve} * @param name name of the curve * @param data the {@link CurveRawData} to create the curve from * @return a YieldAndDiscountCurve */ public static YieldAndDiscountCurve createYieldCurve(String name, CurveRawData data) { List<Tenor> tenors = Lists.newArrayList(data.getCurvePoint()); double[] ttm = new double[tenors.size()]; double[] rates = new double[tenors.size()]; for (int i = 0; i < tenors.size(); i++) { Tenor tenor = tenors.get(i); ttm[i] = tenor.getPeriod().getDays() / 365d; rates[i] = data.getValue(tenor) / 100d; } Interpolator1D interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator( Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); InterpolatedDoublesCurve interpolatedCurve = InterpolatedDoublesCurve.from(ttm, rates, interpolator); return new YieldCurve(name, interpolatedCurve); } /** * Create instance of {@link YieldAndDiscountCurve} * @param name name of the curve * @param data the {@link CurveRawData} to create the curve from * @return a YieldAndDiscountCurve */ public static YieldAndDiscountCurve createIssuerCurve(String name, CurveRawData data) { return createYieldCurve(name, data); } /** * Helper class to store raw curve data */ static class CurveRawData { private TreeMap<Tenor, Double> _doublesCurve = new TreeMap<>(); /** * Add a curve point * * @param curvePoint tenor identifying this point * @param curveValue value at that point as a zero rate */ public void add(Tenor curvePoint, Double curveValue) { ArgumentChecker.notNull(curvePoint, "curvePoint"); ArgumentChecker.notNull(curveValue, "curveValue"); _doublesCurve.put(curvePoint, curveValue); } /** * @return points available on the curve */ public Collection<Tenor> getCurvePoint() { return _doublesCurve.keySet(); } /** * @return all of the raw data points in the curve */ public Collection<Double> getCurveValue() { return _doublesCurve.values(); } /** * @param tenor point on the curve * @return rate of the specified point */ public Double getValue(Tenor tenor) { return _doublesCurve.get(tenor); } } }