All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.loader.csv.RatesCurvesCsvLoader Maven / Gradle / Ivy

There is a newer version: 2.12.46
Show newest version
/*
 * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.loader.csv;

import static com.opengamma.strata.basics.date.DayCounts.ONE_ONE;
import static java.util.stream.Collectors.toList;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Predicate;

import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.CharSource;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.date.DayCount;
import com.opengamma.strata.basics.index.Index;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.Decimal;
import com.opengamma.strata.collect.Messages;
import com.opengamma.strata.collect.io.CharSources;
import com.opengamma.strata.collect.io.CsvFile;
import com.opengamma.strata.collect.io.CsvOutput;
import com.opengamma.strata.collect.io.CsvRow;
import com.opengamma.strata.collect.io.ResourceLocator;
import com.opengamma.strata.collect.io.UnicodeBom;
import com.opengamma.strata.collect.result.ParseFailureException;
import com.opengamma.strata.loader.LoaderUtils;
import com.opengamma.strata.market.ValueType;
import com.opengamma.strata.market.curve.Curve;
import com.opengamma.strata.market.curve.CurveInfoType;
import com.opengamma.strata.market.curve.CurveName;
import com.opengamma.strata.market.curve.InterpolatedNodalCurve;
import com.opengamma.strata.market.curve.RatesCurveGroup;
import com.opengamma.strata.market.curve.RatesCurveGroupDefinition;
import com.opengamma.strata.market.curve.interpolator.CurveExtrapolator;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolator;
import com.opengamma.strata.market.param.DatedParameterMetadata;

/**
 * Loads a set of rates curves into memory by reading from CSV resources.
 * 

* There are three type of CSV files. *

* The first file is the curve group metadata file. * This file has the following header row:
* {@code Group Name, Curve Type, Reference, Curve Name}. *

    *
  • The 'Group Name' column is the name of the group of curves. *
  • The 'Curve Type' column is the type of the curve, "forward" or "discount". *
  • The 'Reference' column is the reference the curve is used for, such as "USD" or "USD-LIBOR-3M". *
  • The 'Curve Name' column is the name of the curve. *
*

* The second file is the curve settings metadata file. * This file has the following header row:
* {@code Curve Name, Value Type, Day Count, Interpolator, Left Extrapolator, Right Extrapolator}. *

    *
  • The 'Curve Name' column is the name of the curve. *
  • The 'Value Type' column is the type of data in the curve, "zero" for zero rates, or "df" for discount factors. *
  • The 'Day Count' column is the name of the day count, such as "Act/365F". *
  • The 'Interpolator' and extrapolator columns define the interpolator to use. *
*

* The third file is the curve values file. * This file has the following header row:
* {@code Valuation Date, Curve Name, Date, Value, Label}. *

    *
  • The 'Valuation Date' column provides the valuation date, allowing data from different * days to be stored in the same file *
  • The 'Curve Name' column is the name of the curve. *
  • The 'Date' column is the date associated with the node. *
  • The 'Value' column is value of the curve at the date. *
  • The 'Label' column is the label used to refer to the node. *
*

* Each curve must be contained entirely within a single file, but each file may contain more than * one curve. The curve points do not need to be ordered. *

* CSV files sometimes contain a Unicode Byte Order Mark. * Callers are responsible for handling this, such as by using {@link UnicodeBom}. */ public final class RatesCurvesCsvLoader { // CSV column headers private static final String SETTINGS_CURVE_NAME = "Curve Name"; private static final String SETTINGS_VALUE_TYPE = "Value Type"; private static final String SETTINGS_DAY_COUNT = "Day Count"; private static final String SETTINGS_INTERPOLATOR = "Interpolator"; private static final String SETTINGS_LEFT_EXTRAPOLATOR = "Left Extrapolator"; private static final String SETTINGS_RIGHT_EXTRAPOLATOR = "Right Extrapolator"; private static final ImmutableList HEADERS_SETTINGS = ImmutableList.of( SETTINGS_CURVE_NAME, SETTINGS_VALUE_TYPE, SETTINGS_DAY_COUNT, SETTINGS_INTERPOLATOR, SETTINGS_LEFT_EXTRAPOLATOR, SETTINGS_RIGHT_EXTRAPOLATOR); private static final String CURVE_DATE = "Valuation Date"; private static final String CURVE_NAME = "Curve Name"; private static final String CURVE_POINT_DATE = "Date"; private static final String CURVE_POINT_VALUE = "Value"; private static final String CURVE_POINT_LABEL = "Label"; private static final ImmutableList HEADERS_NODES = ImmutableList.of( CURVE_DATE, CURVE_NAME, CURVE_POINT_DATE, CURVE_POINT_VALUE, CURVE_POINT_LABEL); /** * Names used in CSV file for value types. */ private static final BiMap VALUE_TYPE_MAP = ImmutableBiMap.of( "zero", ValueType.ZERO_RATE, "df", ValueType.DISCOUNT_FACTOR, "forward", ValueType.FORWARD_RATE, "priceindex", ValueType.PRICE_INDEX); //------------------------------------------------------------------------- /** * Loads one or more CSV format curve files for a specific date. *

* Only those quotes that match the specified date will be loaded. *

* If the files contain a duplicate entry an exception will be thrown. * * @param marketDataDate the curve date to load * @param groupsResource the curve groups CSV resource * @param settingsResource the curve settings CSV resource * @param curveValueResources the CSV resources for curves * @return the loaded curves, mapped by an identifying key * @throws IllegalArgumentException if the files contain a duplicate entry */ public static ImmutableList load( LocalDate marketDataDate, ResourceLocator groupsResource, ResourceLocator settingsResource, Collection curveValueResources) { Collection curveCharSources = curveValueResources.stream().map(r -> r.getCharSource()).collect(toList()); ListMultimap map = parse( d -> marketDataDate.equals(d), groupsResource.getCharSource(), settingsResource.getCharSource(), curveCharSources); return ImmutableList.copyOf(map.get(marketDataDate)); } /** * Loads one or more CSV format curve files for all available dates. *

* If the files contain a duplicate entry an exception will be thrown. * * @param groupsResource the curve groups CSV resource * @param settingsResource the curve settings CSV resource * @param curveValueResources the CSV resources for curves * @return the loaded curves, mapped by date and identifier * @throws IllegalArgumentException if the files contain a duplicate entry */ public static ImmutableListMultimap loadAllDates( ResourceLocator groupsResource, ResourceLocator settingsResource, Collection curveValueResources) { Collection curveCharSources = curveValueResources.stream().map(r -> r.getCharSource()).collect(toList()); return parse(d -> true, groupsResource.getCharSource(), settingsResource.getCharSource(), curveCharSources); } //------------------------------------------------------------------------- /** * Parses one or more CSV format curve files for all available dates. *

* A predicate is specified that is used to filter the dates that are returned. * This could match a single date, a set of dates or all dates. *

* If the files contain a duplicate entry an exception will be thrown. * * @param datePredicate the predicate used to select the dates * @param groupsCharSource the curve groups CSV character source * @param settingsCharSource the curve settings CSV character source * @param curveValueCharSources the CSV character sources for curves * @return the loaded curves, mapped by date and identifier * @throws IllegalArgumentException if the files contain a duplicate entry */ public static ImmutableListMultimap parse( Predicate datePredicate, CharSource groupsCharSource, CharSource settingsCharSource, Collection curveValueCharSources) { List curveGroups = RatesCurveGroupDefinitionCsvLoader.parseCurveGroupDefinitions(groupsCharSource); Map> curves = parseCurves(datePredicate, settingsCharSource, curveValueCharSources); ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); for (RatesCurveGroupDefinition groupDefinition : curveGroups) { for (Map.Entry> entry : curves.entrySet()) { RatesCurveGroup curveGroup = RatesCurveGroup.ofCurves(groupDefinition, entry.getValue().values()); builder.put(entry.getKey(), curveGroup); } } return builder.build(); } //------------------------------------------------------------------------- // loads the curves, filtering by date private static Map> parseCurves( Predicate datePredicate, CharSource settingsResource, Collection curvesResources) { try { // load curve settings Map settingsMap = parseCurveSettings(settingsResource); // load curves, ensuring curves only be seen once within a date Map> resultMap = new TreeMap<>(); for (CharSource curvesResource : curvesResources) { Multimap fileCurvesByDate = parseSingle(datePredicate, curvesResource, settingsMap); // Ensure curve names are unique, with a good error message for (LocalDate date : fileCurvesByDate.keySet()) { Collection fileCurves = fileCurvesByDate.get(date); Map resultCurves = resultMap.computeIfAbsent(date, d -> new HashMap<>()); for (Curve fileCurve : fileCurves) { if (resultCurves.put(fileCurve.getName(), fileCurve) != null) { throw new ParseFailureException( "Error parsing rates curves CSV files: Rates curve loader found multiple curves with the same name: '{value}'", fileCurve.getName()); } } } } return resultMap; } catch (ParseFailureException ex) { throw ex; } catch (RuntimeException ex) { throw new ParseFailureException(ex, "Error parsing rates curves CSV files: {exceptionMessage}", ex.getMessage()); } } //------------------------------------------------------------------------- // loads the curve settings CSV file static Map parseCurveSettings(CharSource settingsResource) { try { ImmutableMap.Builder builder = ImmutableMap.builder(); CsvFile csv = CsvFile.of(settingsResource, true); for (CsvRow row : csv.rows()) { String curveNameStr = row.getField(SETTINGS_CURVE_NAME); String valueTypeStr = row.getField(SETTINGS_VALUE_TYPE); String dayCountStr = row.getField(SETTINGS_DAY_COUNT); String interpolatorStr = row.getField(SETTINGS_INTERPOLATOR); String leftExtrapolatorStr = row.getField(SETTINGS_LEFT_EXTRAPOLATOR); String rightExtrapolatorStr = row.getField(SETTINGS_RIGHT_EXTRAPOLATOR); if (!VALUE_TYPE_MAP.containsKey(valueTypeStr.toLowerCase(Locale.ENGLISH))) { throw new ParseFailureException("Unsupported {} in curve settings '{value}'", SETTINGS_VALUE_TYPE, valueTypeStr); } CurveName curveName = CurveName.of(curveNameStr); ValueType yValueType = VALUE_TYPE_MAP.get(valueTypeStr.toLowerCase(Locale.ENGLISH)); CurveInterpolator interpolator = CurveInterpolator.of(interpolatorStr); CurveExtrapolator leftExtrap = CurveExtrapolator.of(leftExtrapolatorStr); CurveExtrapolator rightExtrap = CurveExtrapolator.of(rightExtrapolatorStr); boolean isPriceIndex = yValueType.equals(ValueType.PRICE_INDEX); ValueType xValueType = isPriceIndex ? ValueType.MONTHS : ValueType.YEAR_FRACTION; DayCount dayCount = isPriceIndex ? ONE_ONE : LoaderUtils.parseDayCount(dayCountStr); LoadedCurveSettings settings = LoadedCurveSettings.of(curveName, xValueType, yValueType, dayCount, interpolator, leftExtrap, rightExtrap); builder.put(curveName, settings); } return builder.build(); } catch (RuntimeException ex) { throw new ParseFailureException( ex, "Error parsing CSV file '{fileName}': {exceptionMessage}", CharSources.extractFileName(settingsResource), ex.getMessage()); } } //------------------------------------------------------------------------- // loads a single curves CSV file // requestedDate can be null, meaning load all dates private static Multimap parseSingle( Predicate datePredicate, CharSource curvesResource, Map settingsMap) { try { CsvFile csv = CsvFile.of(curvesResource, true); Map> allNodes = new HashMap<>(); for (CsvRow row : csv.rows()) { String dateStr = row.getField(CURVE_DATE); String curveNameStr = row.getField(CURVE_NAME); String pointDateStr = row.getField(CURVE_POINT_DATE); String pointValueStr = row.getField(CURVE_POINT_VALUE); String pointLabel = row.getField(CURVE_POINT_LABEL); LocalDate date = LoaderUtils.parseDate(dateStr); if (datePredicate.test(date)) { LocalDate pointDate = LoaderUtils.parseDate(pointDateStr); double pointValue = Double.valueOf(pointValueStr); LoadedCurveKey key = LoadedCurveKey.of(date, CurveName.of(curveNameStr)); List curveNodes = allNodes.computeIfAbsent(key, k -> new ArrayList<>()); curveNodes.add(LoadedCurveNode.of(pointDate, pointValue, pointLabel)); } } return buildCurves(settingsMap, allNodes); } catch (RuntimeException ex) { throw new ParseFailureException( ex, "Error parsing rates curve CSV file '{fileName}': {exceptionMessage}", CharSources.extractFileName(curvesResource), ex.getMessage()); } } // build the curves private static Multimap buildCurves( Map settingsMap, Map> allNodes) { ImmutableMultimap.Builder results = ImmutableMultimap.builder(); for (Map.Entry> entry : allNodes.entrySet()) { LoadedCurveKey key = entry.getKey(); LoadedCurveSettings settings = settingsMap.get(key.getCurveName()); if (settings == null) { throw new ParseFailureException("Missing settings for curve '{value}'", key); } results.put(key.getCurveDate(), settings.createCurve(key.getCurveDate(), entry.getValue())); } return results.build(); } //------------------------------------------------------------------------- /** * Writes the curve settings in a CSV format to a file. * * @param file the file * @param group the curve group */ public static void writeCurveSettings(File file, RatesCurveGroup group) { try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { writeCurveSettings(writer, group); } catch (IOException ex) { throw new UncheckedIOException(ex); } } /** * Writes the curve settings in a CSV format to an appendable. * * @param underlying the underlying appendable destination * @param group the curve group */ public static void writeCurveSettings(Appendable underlying, RatesCurveGroup group) { CsvOutput csv = CsvOutput.standard(underlying); // header csv.writeLine(HEADERS_SETTINGS); // rows Map discountingCurves = group.getDiscountCurves(); Set names = new HashSet<>(); for (Entry entry : discountingCurves.entrySet()) { Curve curve = entry.getValue(); csv.writeLine(curveSettings(curve)); names.add(curve.getName()); } Map forwardCurves = group.getForwardCurves(); for (Entry entry : forwardCurves.entrySet()) { Curve curve = entry.getValue(); if (!names.contains(curve.getName())) { csv.writeLine(curveSettings(curve)); names.add(curve.getName()); } } } private static List curveSettings(Curve curve) { ArgChecker.isTrue(curve instanceof InterpolatedNodalCurve, "Curve must be an InterpolatedNodalCurve"); if (!VALUE_TYPE_MAP.inverse().containsKey(curve.getMetadata().getYValueType())) { throw new IllegalArgumentException( Messages.format("Unsupported ValueType in curve settings: {}", curve.getMetadata().getYValueType())); } InterpolatedNodalCurve interpolatedCurve = (InterpolatedNodalCurve) curve; List line = new ArrayList<>(); line.add(curve.getName().getName()); line.add(VALUE_TYPE_MAP.inverse().get(curve.getMetadata().getYValueType())); line.add(curve.getMetadata().getInfo(CurveInfoType.DAY_COUNT).toString()); line.add(interpolatedCurve.getInterpolator().toString()); line.add(interpolatedCurve.getExtrapolatorLeft().toString()); line.add(interpolatedCurve.getExtrapolatorRight().toString()); return line; } //------------------------------------------------------------------------- /** * Writes the curve groups definition in a CSV format to a file. * * @param file the file * @param valuationDate the valuation date * @param group the curve group */ public static void writeCurveNodes(File file, LocalDate valuationDate, RatesCurveGroup group) { try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { writeCurveNodes(writer, valuationDate, group); } catch (IOException ex) { throw new UncheckedIOException(ex); } } /** * Writes the curve nodes in a CSV format to an appendable. * * @param underlying the underlying appendable destination * @param valuationDate the valuation date * @param group the curve group */ public static void writeCurveNodes(Appendable underlying, LocalDate valuationDate, RatesCurveGroup group) { CsvOutput csv = CsvOutput.standard(underlying); // header csv.writeLine(HEADERS_NODES); // rows String valuationDateStr = valuationDate.toString(); Map discountingCurves = group.getDiscountCurves(); Set names = new HashSet<>(); for (Entry entry : discountingCurves.entrySet()) { Curve curve = entry.getValue(); nodeLines(valuationDateStr, curve, csv); names.add(curve.getName()); } Map forwardCurves = group.getForwardCurves(); for (Entry entry : forwardCurves.entrySet()) { Curve curve = entry.getValue(); if (!names.contains(curve.getName())) { nodeLines(valuationDateStr, curve, csv); names.add(curve.getName()); } } } // add each node to the csv file private static void nodeLines(String valuationDateStr, Curve curve, CsvOutput csv) { ArgChecker.isTrue(curve instanceof InterpolatedNodalCurve, "interpolated"); InterpolatedNodalCurve interpolatedCurve = (InterpolatedNodalCurve) curve; int nbPoints = interpolatedCurve.getXValues().size(); for (int i = 0; i < nbPoints; i++) { ArgChecker.isTrue( interpolatedCurve.getParameterMetadata(i) instanceof DatedParameterMetadata, "Curve metadata must contain a date, but was " + interpolatedCurve.getParameterMetadata(i).getClass().getSimpleName()); DatedParameterMetadata metadata = (DatedParameterMetadata) interpolatedCurve.getParameterMetadata(i); List line = new ArrayList<>(); line.add(valuationDateStr); line.add(curve.getName().getName().toString()); line.add(metadata.getDate().toString()); line.add(Decimal.of(interpolatedCurve.getYValues().get(i)).formatAtLeast(1)); line.add(metadata.getLabel()); csv.writeLine(line); } } //------------------------------------------------------------------------- /** * Restricted constructor. */ private RatesCurvesCsvLoader() { } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy