/** * 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.collect.TestHelper.coverBeanEquals; import static com.opengamma.strata.collect.TestHelper.coverImmutableBean; import static com.opengamma.strata.collect.TestHelper.coverPrivateConstructor; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.time.LocalDate; import java.util.List; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.basics.index.IborIndices; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.collect.io.ResourceLocator; import com.opengamma.strata.market.ValueType; import com.opengamma.strata.market.curve.Curve; import com.opengamma.strata.market.curve.CurveGroup; import com.opengamma.strata.market.curve.CurveGroupName; import com.opengamma.strata.market.curve.CurveName; import com.opengamma.strata.market.curve.InterpolatedNodalCurve; import com.opengamma.strata.market.curve.interpolator.CurveExtrapolators; import com.opengamma.strata.market.curve.interpolator.CurveInterpolators; import com.opengamma.strata.market.param.ParameterMetadata; /** * Test {@link RatesCurvesCsvLoader}. */ @Test public class RatesCurvesCsvLoaderTest { private static final String GROUPS_1 = "classpath:com/opengamma/strata/loader/csv/groups.csv"; private static final String SETTINGS_1 = "classpath:com/opengamma/strata/loader/csv/settings.csv"; private static final String CURVES_1 = "classpath:com/opengamma/strata/loader/csv/curves-1.csv"; private static final String CURVES_2 = "classpath:com/opengamma/strata/loader/csv/curves-2.csv"; private static final String CURVES_3 = "classpath:com/opengamma/strata/loader/csv/curves-3.csv"; private static final String CURVES_1_AND_2 = "classpath:com/opengamma/strata/loader/csv/curves-1-and-2.csv"; private static final String SETTINGS_INVALID_DAY_COUNT = "classpath:com/opengamma/strata/loader/csv/settings-invalid-day-count.csv"; private static final String SETTINGS_INVALID_INTERPOLATOR = "classpath:com/opengamma/strata/loader/csv/settings-invalid-interpolator.csv"; private static final String SETTINGS_INVALID_LEFT_EXTRAPOLATOR = "classpath:com/opengamma/strata/loader/csv/settings-invalid-left-extrapolator.csv"; private static final String SETTINGS_INVALID_RIGHT_EXTRAPOLATOR = "classpath:com/opengamma/strata/loader/csv/settings-invalid-right-extrapolator.csv"; private static final String SETTINGS_INVALID_MISSING_COLUMN = "classpath:com/opengamma/strata/loader/csv/settings-invalid-missing-column.csv"; private static final String SETTINGS_INVALID_VALUE_TYPE = "classpath:com/opengamma/strata/loader/csv/settings-invalid-value-type.csv"; private static final String SETTINGS_EMPTY = "classpath:com/opengamma/strata/loader/csv/settings-empty.csv"; private static final String GROUPS_INVALID_CURVE_TYPE = "classpath:com/opengamma/strata/loader/csv/groups-invalid-curve-type.csv"; private static final String GROUPS_INVALID_REFERENCE_INDEX = "classpath:com/opengamma/strata/loader/csv/groups-invalid-reference-index.csv"; private static final String CURVES_INVALID_DUPLICATE_POINTS = "classpath:com/opengamma/strata/loader/csv/curves-invalid-duplicate-points.csv"; // curve date used in the test data private static final LocalDate CURVE_DATE = LocalDate.of(2009, 7, 31); private static final LocalDate CURVE_DATE_CURVES_3 = LocalDate.of(2009, 7, 30); // tolerance private static final double TOLERANCE = 1.0E-4; //------------------------------------------------------------------------- @Test(expectedExceptions = IllegalArgumentException.class) public void test_missing_settings_file() { testSettings("classpath:invalid"); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Header not found: Curve Name") public void test_invalid_settings_missing_column_file() { testSettings(SETTINGS_INVALID_MISSING_COLUMN); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "DayCount name not found: Act") public void test_invalid_settings_day_count_file() { testSettings(SETTINGS_INVALID_DAY_COUNT); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "CurveInterpolator name not found: Wacky") public void test_invalid_settings_interpolator_file() { testSettings(SETTINGS_INVALID_INTERPOLATOR); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "CurveExtrapolator name not found: Polynomial") public void test_invalid_settings_left_extrapolator_file() { testSettings(SETTINGS_INVALID_LEFT_EXTRAPOLATOR); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "CurveExtrapolator name not found: Polynomial") public void test_invalid_settings_right_extrapolator_file() { testSettings(SETTINGS_INVALID_RIGHT_EXTRAPOLATOR); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Unsupported Value Type in curve settings: DS") public void test_invalid_settings_value_type_file() { testSettings(SETTINGS_INVALID_VALUE_TYPE); } private void testSettings(String settingsResource) { RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(settingsResource), ImmutableList.of(ResourceLocator.of(CURVES_1))); } //------------------------------------------------------------------------- @Test(expectedExceptions = IllegalArgumentException.class) public void test_missing_groups_file() { testGroups("classpath:invalid"); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Unsupported curve type: Inflation") public void test_invalid_groups_curve_type_file() { testGroups(GROUPS_INVALID_CURVE_TYPE); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "No index found for reference: LIBOR") public void test_invalid_groups_reference_index_file() { testGroups(GROUPS_INVALID_REFERENCE_INDEX); } private void testGroups(String groupsResource) { RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(groupsResource), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1))); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Missing settings for curve: .*") public void test_noSettings() { List<CurveGroup> curveGroups = RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_EMPTY), ImmutableList.of(ResourceLocator.of(CURVES_1))); assertEquals(curveGroups.size(), 1); CurveGroup curveGroup = Iterables.getOnlyElement(curveGroups); assertUsdDisc(curveGroup.findDiscountCurve(Currency.USD).get()); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Rates curve loader found multiple curves with the same name: .*") public void test_single_curve_multiple_Files() { RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1), ResourceLocator.of(CURVES_1))); } public void test_multiple_curves_single_file() { List<CurveGroup> curveGroups = RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1_AND_2))); assertCurves(curveGroups); } public void test_multiple_curves_multiple_files() { List<CurveGroup> curveGroups = RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1), ResourceLocator.of(CURVES_2))); assertCurves(curveGroups); } @Test(expectedExceptions = IllegalArgumentException.class) public void test_invalid_curve_duplicate_points() { RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_INVALID_DUPLICATE_POINTS))); } //------------------------------------------------------------------------- public void test_load_all_curves() { ListMultimap<LocalDate, CurveGroup> allGroups = RatesCurvesCsvLoader.loadAllDates( ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1), ResourceLocator.of(CURVES_2), ResourceLocator.of(CURVES_3))); assertEquals(allGroups.size(), 2); assertCurves(allGroups.get(CURVE_DATE)); List<CurveGroup> curves3 = allGroups.get(CURVE_DATE_CURVES_3); assertEquals(curves3.size(), 1); CurveGroup group = curves3.get(0); // All curve points are set to 0 in test data to ensure these are really different curve instances Curve usdDisc = group.findDiscountCurve(Currency.USD).get(); InterpolatedNodalCurve usdDiscNodal = (InterpolatedNodalCurve) usdDisc; assertEquals(usdDiscNodal.getMetadata().getCurveName(), CurveName.of("USD-Disc")); assertTrue(usdDiscNodal.getYValues().equalZeroWithTolerance(0d)); Curve usd3ml = group.findForwardCurve(IborIndices.USD_LIBOR_3M).get(); InterpolatedNodalCurve usd3mlNodal = (InterpolatedNodalCurve) usd3ml; assertEquals(usd3mlNodal.getMetadata().getCurveName(), CurveName.of("USD-3ML")); assertTrue(usd3mlNodal.getYValues().equalZeroWithTolerance(0d)); } public void test_load_curves_date_filtering() { List<CurveGroup> curves = RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1), ResourceLocator.of(CURVES_2), ResourceLocator.of(CURVES_3))); assertCurves(curves); } //------------------------------------------------------------------------- private void assertCurves(List<CurveGroup> curveGroups) { assertNotNull(curveGroups); assertEquals(curveGroups.size(), 1); CurveGroup curveGroup = curveGroups.get(0); assertEquals(curveGroup.getName(), CurveGroupName.of("Default")); assertUsdDisc(curveGroup.findDiscountCurve(Currency.USD).get()); Curve usd3ml = curveGroup.findForwardCurve(IborIndices.USD_LIBOR_3M).get(); assertUsd3ml(usd3ml); } private void assertUsdDisc(Curve curve) { assertTrue(curve instanceof InterpolatedNodalCurve); InterpolatedNodalCurve nodalCurve = (InterpolatedNodalCurve) curve; assertEquals(nodalCurve.getMetadata().getCurveName(), CurveName.of("USD-Disc")); LocalDate valuationDate = LocalDate.of(2009, 7, 31); LocalDate[] nodeDates = new LocalDate[] { LocalDate.of(2009, 11, 6), LocalDate.of(2010, 2, 8), LocalDate.of(2010, 8, 6), LocalDate.of(2011, 8, 8), LocalDate.of(2012, 8, 8), LocalDate.of(2014, 8, 6), LocalDate.of(2019, 8, 7) }; String[] labels = new String[] {"3M", "6M", "1Y", "2Y", "3Y", "5Y", "10Y"}; for (int i = 0; i < nodalCurve.getXValues().size(); i++) { LocalDate nodeDate = nodeDates[i]; double actualYearFraction = nodalCurve.getXValues().get(i); double expectedYearFraction = getYearFraction(valuationDate, nodeDate); assertThat(actualYearFraction).isCloseTo(expectedYearFraction, offset(TOLERANCE)); ParameterMetadata nodeMetadata = nodalCurve.getMetadata().getParameterMetadata().get().get(i); assertEquals(nodeMetadata.getLabel(), labels[i]); } DoubleArray expectedYValues = DoubleArray.of( 0.001763775, 0.002187884, 0.004437206, 0.011476741, 0.017859057, 0.026257102, 0.035521988); assertEquals(nodalCurve.getYValues(), expectedYValues); } private void assertUsd3ml(Curve curve) { assertTrue(curve instanceof InterpolatedNodalCurve); InterpolatedNodalCurve nodalCurve = (InterpolatedNodalCurve) curve; assertEquals(nodalCurve.getMetadata().getCurveName(), CurveName.of("USD-3ML")); LocalDate valuationDate = LocalDate.of(2009, 7, 31); LocalDate[] nodeDates = new LocalDate[] { LocalDate.of(2009, 11, 4), LocalDate.of(2010, 8, 4), LocalDate.of(2011, 8, 4), LocalDate.of(2012, 8, 6), LocalDate.of(2014, 8, 5), LocalDate.of(2019, 8, 6) }; String[] labels = new String[] {"3M", "1Y", "2Y", "3Y", "5Y", "10Y"}; for (int i = 0; i < nodalCurve.getXValues().size(); i++) { LocalDate nodeDate = nodeDates[i]; double actualYearFraction = nodalCurve.getXValues().get(i); double expectedYearFraction = getYearFraction(valuationDate, nodeDate); assertThat(actualYearFraction).isCloseTo(expectedYearFraction, offset(TOLERANCE)); ParameterMetadata nodeMetadata = nodalCurve.getMetadata().getParameterMetadata().get().get(i); assertEquals(nodeMetadata.getLabel(), labels[i]); } DoubleArray expectedYValues = DoubleArray.of( 0.007596889, 0.008091541, 0.015244398, 0.021598026, 0.029984216, 0.039245812); assertEquals(nodalCurve.getYValues(), expectedYValues); } private double getYearFraction(LocalDate fromDate, LocalDate toDate) { return DayCounts.ACT_ACT_ISDA.yearFraction(fromDate, toDate); } //------------------------------------------------------------------------- public void test_writer_curve_settings() { List<CurveGroup> curveGroups = RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1), ResourceLocator.of(CURVES_2))); Appendable underlying = new StringBuilder(); RatesCurvesCsvLoader.writeCurveSettings(underlying, curveGroups.get(0)); String created = underlying.toString(); String expected = "Curve Name,Value Type,Day Count,Interpolator,Left Extrapolator,Right Extrapolator" + System.lineSeparator() + "USD-Disc,zero,Act/Act ISDA,Linear,Flat,Flat" + System.lineSeparator() + "USD-3ML,zero,Act/Act ISDA,Linear,Flat,Flat" + System.lineSeparator(); assertEquals(created, expected); } public void test_writer_curve_nodes() { List<CurveGroup> curveGroups = RatesCurvesCsvLoader.load( CURVE_DATE, ResourceLocator.of(GROUPS_1), ResourceLocator.of(SETTINGS_1), ImmutableList.of(ResourceLocator.of(CURVES_1), ResourceLocator.of(CURVES_2))); Appendable underlying = new StringBuilder(); RatesCurvesCsvLoader.writeCurveNodes(underlying, CURVE_DATE, curveGroups.get(0)); String created = underlying.toString(); String expected = "Valuation Date,Curve Name,Date,Value,Label" + System.lineSeparator() + "2009-07-31,USD-Disc,2009-11-06,0.001763775,3M" + System.lineSeparator() + "2009-07-31,USD-Disc,2010-02-08,0.002187884,6M" + System.lineSeparator() + "2009-07-31,USD-Disc,2010-08-06,0.004437206,1Y" + System.lineSeparator() + "2009-07-31,USD-Disc,2011-08-08,0.011476741,2Y" + System.lineSeparator() + "2009-07-31,USD-Disc,2012-08-08,0.017859057,3Y" + System.lineSeparator() + "2009-07-31,USD-Disc,2014-08-06,0.026257102,5Y" + System.lineSeparator() + "2009-07-31,USD-Disc,2019-08-07,0.035521988,10Y" + System.lineSeparator() + "2009-07-31,USD-3ML,2009-11-04,0.007596889,3M" + System.lineSeparator() + "2009-07-31,USD-3ML,2010-08-04,0.008091541,1Y" + System.lineSeparator() + "2009-07-31,USD-3ML,2011-08-04,0.015244398,2Y" + System.lineSeparator() + "2009-07-31,USD-3ML,2012-08-06,0.021598026,3Y" + System.lineSeparator() + "2009-07-31,USD-3ML,2014-08-05,0.029984216,5Y" + System.lineSeparator() + "2009-07-31,USD-3ML,2019-08-06,0.039245812,10Y" + System.lineSeparator(); assertEquals(created, expected); } //------------------------------------------------------------------------- public void coverage() { coverPrivateConstructor(RatesCurvesCsvLoader.class); LoadedCurveKey.meta(); coverImmutableBean(LoadedCurveKey.of(CURVE_DATE, CurveName.of("Test"))); LoadedCurveNode.meta(); coverImmutableBean(LoadedCurveNode.of(CURVE_DATE, 1d, "Test")); LoadedCurveSettings.meta(); LoadedCurveSettings settings1 = LoadedCurveSettings.of( CurveName.of("Test"), ValueType.YEAR_FRACTION, ValueType.ZERO_RATE, DayCounts.ACT_365F, CurveInterpolators.LINEAR, CurveExtrapolators.FLAT, CurveExtrapolators.FLAT); LoadedCurveSettings settings2 = LoadedCurveSettings.of( CurveName.of("Test2"), ValueType.YEAR_FRACTION, ValueType.DISCOUNT_FACTOR, DayCounts.ACT_ACT_ISDA, CurveInterpolators.LOG_LINEAR, CurveExtrapolators.LINEAR, CurveExtrapolators.LINEAR); coverImmutableBean(settings1); coverBeanEquals(settings1, settings2); } }