com.opengamma.strata.loader.csv.FxRatesCsvLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.strata.loader.csv.FxRatesCsvLoader.java

Source

/**
 * 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 java.util.stream.Collectors.toList;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.io.CharSource;
import com.opengamma.strata.basics.currency.CurrencyPair;
import com.opengamma.strata.basics.currency.FxRate;
import com.opengamma.strata.collect.Messages;
import com.opengamma.strata.collect.io.CsvFile;
import com.opengamma.strata.collect.io.CsvRow;
import com.opengamma.strata.collect.io.ResourceLocator;
import com.opengamma.strata.data.FxRateId;

/**
 * Loads a set of FX rates into memory from CSV resources.
 * <p>
 * The rates are expected to be in a CSV format, with the following header row:<br />
 * {@code Valuation Date, Currency Pair, Value}.
 * <ul>
 * <li>The 'Valuation Date' column provides the valuation date, allowing data from different
 *  days to be stored in the same file
 * <li>The 'Currency Pair' column is the currency pair in the format 'EUR/USD'.
 * <li>The 'Value' column is the value of the rate.
 * </ul>
 * <p>
 * Each file may contain entries for many different dates.
 * <p>
 * For example:
 * <pre>
 * Valuation Date, Currency Pair, Value
 * 2014-01-22, EUR/USD, 1.10
 * 2014-01-22, GBP/USD, 1.50
 * 2014-01-23, EUR/USD, 1.11
 * </pre>
 * Note that Microsoft Excel prefers the CSV file to have no space after the comma.
 */
public final class FxRatesCsvLoader {

    // CSV column headers
    private static final String DATE_FIELD = "Valuation Date";
    private static final String CURRENCY_PAIR_FIELD = "Currency Pair";
    private static final String VALUE_FIELD = "Value";

    //-------------------------------------------------------------------------
    /**
     * Loads one or more CSV format FX rate files for a specific date.
     * <p>
     * Only those rates that match the specified date will be loaded.
     * <p>
     * If the files contain a duplicate entry an exception will be thrown.
     * 
     * @param marketDataDate  the date to load
     * @param resources  the CSV resources
     * @return the loaded FX rates, mapped by {@linkplain FxRateId rate ID}
     * @throws IllegalArgumentException if the files contain a duplicate entry
     */
    public static ImmutableMap<FxRateId, FxRate> load(LocalDate marketDataDate, ResourceLocator... resources) {
        return load(marketDataDate, Arrays.asList(resources));
    }

    /**
     * Loads one or more CSV format FX rate files for a specific date.
     * <p>
     * Only those rates that match the specified date will be loaded.
     * <p>
     * If the files contain a duplicate entry an exception will be thrown.
     * 
     * @param marketDataDate  the date to load
     * @param resources  the CSV resources
     * @return the loaded FX rates, mapped by {@linkplain FxRateId rate ID}
     * @throws IllegalArgumentException if the files contain a duplicate entry
     */
    public static ImmutableMap<FxRateId, FxRate> load(LocalDate marketDataDate,
            Collection<ResourceLocator> resources) {
        Collection<CharSource> charSources = resources.stream().map(r -> r.getCharSource()).collect(toList());
        return parse(d -> marketDataDate.equals(d), charSources).getOrDefault(marketDataDate, ImmutableMap.of());
    }

    //-------------------------------------------------------------------------
    /**
     * Loads one or more CSV format FX rate files for a set of dates.
     * <p>
     * Only those rates that match one of the specified dates will be loaded.
     * <p>
     * If the files contain a duplicate entry an exception will be thrown.
     * 
     * @param marketDataDates  the set of dates to load
     * @param resources  the CSV resources
     * @return the loaded FX rates, mapped by {@link LocalDate} and {@linkplain FxRateId rate ID}
     * @throws IllegalArgumentException if the files contain a duplicate entry
     */
    public static ImmutableMap<LocalDate, ImmutableMap<FxRateId, FxRate>> load(Set<LocalDate> marketDataDates,
            ResourceLocator... resources) {

        return load(marketDataDates, Arrays.asList(resources));
    }

    /**
     * Loads one or more CSV format FX rate files for a set of dates.
     * <p>
     * Only those rates that match one of the specified dates will be loaded.
     * <p>
     * If the files contain a duplicate entry an exception will be thrown.
     * 
     * @param marketDataDates  the dates to load
     * @param resources  the CSV resources
     * @return the loaded FX rates, mapped by {@link LocalDate} and {@linkplain FxRateId rate ID}
     * @throws IllegalArgumentException if the files contain a duplicate entry
     */
    public static ImmutableMap<LocalDate, ImmutableMap<FxRateId, FxRate>> load(Set<LocalDate> marketDataDates,
            Collection<ResourceLocator> resources) {

        Collection<CharSource> charSources = resources.stream().map(r -> r.getCharSource()).collect(toList());
        return parse(d -> marketDataDates.contains(d), charSources);
    }

    //-------------------------------------------------------------------------
    /**
     * Loads one or more CSV format FX rate files.
     * <p>
     * All dates that are found will be returned.
     * <p>
     * If the files contain a duplicate entry an exception will be thrown.
     * 
     * @param resources  the CSV resources
     * @return the loaded FX rates, mapped by {@link LocalDate} and {@linkplain FxRateId rate ID}
     * @throws IllegalArgumentException if the files contain a duplicate entry
     */
    public static ImmutableMap<LocalDate, ImmutableMap<FxRateId, FxRate>> loadAllDates(
            ResourceLocator... resources) {
        return loadAllDates(Arrays.asList(resources));
    }

    /**
     * Loads one or more CSV format FX rate files.
     * <p>
     * All dates that are found will be returned.
     * <p>
     * If the files contain a duplicate entry an exception will be thrown.
     * 
     * @param resources  the CSV resources
     * @return the loaded FX rates, mapped by {@link LocalDate} and {@linkplain FxRateId rate ID}
     * @throws IllegalArgumentException if the files contain a duplicate entry
     */
    public static ImmutableMap<LocalDate, ImmutableMap<FxRateId, FxRate>> loadAllDates(
            Collection<ResourceLocator> resources) {

        Collection<CharSource> charSources = resources.stream().map(r -> r.getCharSource()).collect(toList());
        return parse(d -> true, charSources);
    }

    //-------------------------------------------------------------------------
    /**
     * Parses one or more CSV format FX rate files.
     * <p>
     * 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.
     * <p>
     * If the files contain a duplicate entry an exception will be thrown.
     * 
     * @param datePredicate  the predicate used to select the dates
     * @param charSources  the CSV character sources
     * @return the loaded FX rates, mapped by {@link LocalDate} and {@linkplain FxRateId rate ID}
     * @throws IllegalArgumentException if the files contain a duplicate entry
     */
    public static ImmutableMap<LocalDate, ImmutableMap<FxRateId, FxRate>> parse(Predicate<LocalDate> datePredicate,
            Collection<CharSource> charSources) {

        // builder ensures keys can only be seen once
        Map<LocalDate, ImmutableMap.Builder<FxRateId, FxRate>> mutableMap = new HashMap<>();
        for (CharSource charSource : charSources) {
            parseSingle(datePredicate, charSource, mutableMap);
        }
        ImmutableMap.Builder<LocalDate, ImmutableMap<FxRateId, FxRate>> builder = ImmutableMap.builder();
        for (Entry<LocalDate, Builder<FxRateId, FxRate>> entry : mutableMap.entrySet()) {
            builder.put(entry.getKey(), entry.getValue().build());
        }
        return builder.build();
    }

    // loads a single CSV file, filtering by date
    private static void parseSingle(Predicate<LocalDate> datePredicate, CharSource resource,
            Map<LocalDate, ImmutableMap.Builder<FxRateId, FxRate>> mutableMap) {

        try {
            CsvFile csv = CsvFile.of(resource, true);
            for (CsvRow row : csv.rows()) {
                String dateText = row.getField(DATE_FIELD);
                LocalDate date = LocalDate.parse(dateText);
                if (datePredicate.test(date)) {
                    String currencyPairStr = row.getField(CURRENCY_PAIR_FIELD);
                    String valueStr = row.getField(VALUE_FIELD);
                    CurrencyPair currencyPair = CurrencyPair.parse(currencyPairStr);
                    double value = Double.valueOf(valueStr);

                    ImmutableMap.Builder<FxRateId, FxRate> builderForDate = mutableMap.computeIfAbsent(date,
                            k -> ImmutableMap.builder());
                    builderForDate.put(FxRateId.of(currencyPair), FxRate.of(currencyPair, value));
                }
            }
        } catch (RuntimeException ex) {
            throw new IllegalArgumentException(
                    Messages.format("Error processing resource as CSV file: {}", resource), ex);
        }
    }

    //-------------------------------------------------------------------------
    /**
     * Restricted constructor.
     */
    private FxRatesCsvLoader() {
    }
}