/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.market.curve; import static com.opengamma.strata.basics.currency.Currency.GBP; import static com.opengamma.strata.basics.date.DayCounts.ACT_365F; import static com.opengamma.strata.basics.index.IborIndices.GBP_LIBOR_1M; import static com.opengamma.strata.basics.index.IborIndices.GBP_LIBOR_1W; import static com.opengamma.strata.basics.index.IborIndices.GBP_LIBOR_3M; import static com.opengamma.strata.basics.index.OvernightIndices.GBP_SONIA; import static com.opengamma.strata.basics.index.PriceIndices.GB_RPI; import static com.opengamma.strata.collect.TestHelper.assertSerialization; import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg; import static com.opengamma.strata.collect.TestHelper.coverBeanEquals; import static com.opengamma.strata.collect.TestHelper.coverImmutableBean; import static com.opengamma.strata.collect.TestHelper.date; import static com.opengamma.strata.market.curve.CurveNodeClashAction.DROP_THIS; import static com.opengamma.strata.product.swap.type.FixedInflationSwapConventions.GBP_FIXED_ZC_GB_RPI; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import java.time.LocalDate; import java.time.Period; import java.time.YearMonth; import java.util.List; import java.util.Map; import java.util.Optional; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.StandardId; import com.opengamma.strata.basics.date.Tenor; import com.opengamma.strata.basics.index.Index; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries; import com.opengamma.strata.data.ImmutableMarketData; import com.opengamma.strata.data.MarketData; import com.opengamma.strata.data.ObservableId; import com.opengamma.strata.market.ShiftType; import com.opengamma.strata.market.ValueType; import com.opengamma.strata.market.curve.interpolator.CurveExtrapolators; import com.opengamma.strata.market.curve.interpolator.CurveInterpolators; import com.opengamma.strata.market.curve.node.FixedInflationSwapCurveNode; import com.opengamma.strata.market.observable.QuoteId; import com.opengamma.strata.product.Trade; import com.opengamma.strata.product.swap.type.FixedInflationSwapTemplate; /** * Test {@link CurveGroupDefinition}. */ @Test public class CurveGroupDefinitionTest { private static final ReferenceData REF_DATA = ReferenceData.standard(); private static final ObservableId GBP_LIBOR_1M_ID = QuoteId.of(StandardId.of("OG", "Ticker1")); private static final ObservableId GBP_LIBOR_3M_ID = QuoteId.of(StandardId.of("OG", "Ticker3")); private static final DummyFraCurveNode NODE1 = DummyFraCurveNode.of(Period.ofMonths(1), GBP_LIBOR_1M, GBP_LIBOR_1M_ID); private static final DummyFraCurveNode NODE2 = DummyFraCurveNode.of(Period.ofMonths(3), GBP_LIBOR_3M, GBP_LIBOR_3M_ID); private static final FixedInflationSwapCurveNode NODE_I1 = FixedInflationSwapCurveNode.of(FixedInflationSwapTemplate.of(Tenor.TENOR_5Y, GBP_FIXED_ZC_GB_RPI), GBP_LIBOR_1M_ID); private static final FixedInflationSwapCurveNode NODE_I2 = FixedInflationSwapCurveNode.of(FixedInflationSwapTemplate.of(Tenor.TENOR_10Y, GBP_FIXED_ZC_GB_RPI), GBP_LIBOR_1M_ID); private static final CurveNodeDateOrder DROP_THIS_2D = CurveNodeDateOrder.of(2, DROP_THIS); private static final CurveName CURVE_NAME1 = CurveName.of("Test"); private static final CurveName CURVE_NAME2 = CurveName.of("Test2"); private static final CurveName CURVE_NAME_I = CurveName.of("Test-CPI"); private static final InterpolatedNodalCurveDefinition CURVE_DEFN1 = InterpolatedNodalCurveDefinition.builder() .name(CURVE_NAME1) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.ZERO_RATE) .dayCount(ACT_365F) .nodes(ImmutableList.of(NODE1, NODE2)) .interpolator(CurveInterpolators.LINEAR) .extrapolatorLeft(CurveExtrapolators.FLAT) .extrapolatorRight(CurveExtrapolators.FLAT) .build(); private static final InterpolatedNodalCurveDefinition CURVE_DEFN2 = CURVE_DEFN1.toBuilder() .name(CURVE_NAME2) .build(); private static final InterpolatedNodalCurveDefinition CURVE_DEFN_I = InterpolatedNodalCurveDefinition.builder() .name(CURVE_NAME_I) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.PRICE_INDEX) .nodes(ImmutableList.of(NODE_I1, NODE_I2)) .interpolator(CurveInterpolators.LOG_LINEAR) .extrapolatorLeft(CurveExtrapolators.FLAT) .extrapolatorRight(CurveExtrapolators.FLAT) .build(); private static final CurveGroupEntry ENTRY1 = CurveGroupEntry.builder() .curveName(CURVE_NAME1) .discountCurrencies(GBP) .indices(GBP_LIBOR_1W, GBP_SONIA) .build(); private static final CurveGroupEntry ENTRY2 = CurveGroupEntry.builder() .curveName(CURVE_NAME2) .indices(GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); private static final CurveGroupEntry ENTRY3 = CurveGroupEntry.builder() .curveName(CURVE_NAME1) .discountCurrencies(GBP) .indices(GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); private static final DoubleArray SEASONALITY_ADDITIVE = DoubleArray.of( 1.0, 1.5, 1.0, -0.5, -0.5, -1.0, -1.5, 0.0, 0.5, 1.0, 1.0, -2.5); private static final SeasonalityDefinition SEASONALITY_ADDITIVE_DEF = SeasonalityDefinition.of(SEASONALITY_ADDITIVE, ShiftType.SCALED); public void test_builder1() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addDiscountCurve(CURVE_DEFN1, GBP) .addForwardCurve(CURVE_DEFN1, GBP_SONIA) .addForwardCurve(CURVE_DEFN1, GBP_LIBOR_1W) .addForwardCurve(CURVE_DEFN2, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); assertEquals(test.getName(), CurveGroupName.of("Test")); assertEquals(test.getEntries(), ImmutableList.of(ENTRY1, ENTRY2)); assertEquals(test.findEntry(CurveName.of("Test")), Optional.of(ENTRY1)); assertEquals(test.findEntry(CurveName.of("Test2")), Optional.of(ENTRY2)); assertEquals(test.findEntry(CurveName.of("Rubbish")), Optional.empty()); assertEquals(test.findCurveDefinition(CurveName.of("Test")), Optional.of(CURVE_DEFN1)); assertEquals(test.findCurveDefinition(CurveName.of("Test2")), Optional.of(CURVE_DEFN2)); assertEquals(test.findCurveDefinition(CurveName.of("Rubbish")), Optional.empty()); } public void test_builder2() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_DEFN1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); assertEquals(test.getName(), CurveGroupName.of("Test")); assertEquals(test.getEntries(), ImmutableList.of(ENTRY3)); assertEquals(test.findEntry(CurveName.of("Test")), Optional.of(ENTRY3)); assertEquals(test.findEntry(CurveName.of("Test2")), Optional.empty()); assertEquals(test.findEntry(CurveName.of("Rubbish")), Optional.empty()); assertEquals(test.findCurveDefinition(CurveName.of("Test")), Optional.of(CURVE_DEFN1)); assertEquals(test.findCurveDefinition(CurveName.of("Test2")), Optional.empty()); assertEquals(test.findCurveDefinition(CurveName.of("Rubbish")), Optional.empty()); } public void test_builder_seasonality() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_DEFN1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .addSeasonality(CURVE_NAME_I, SEASONALITY_ADDITIVE_DEF) .build(); assertEquals(test.getName(), CurveGroupName.of("Test")); assertEquals(test.getEntries(), ImmutableList.of(ENTRY3)); assertEquals(test.findEntry(CurveName.of("Test")), Optional.of(ENTRY3)); assertEquals(test.findEntry(CurveName.of("Test2")), Optional.empty()); assertEquals(test.findEntry(CurveName.of("Rubbish")), Optional.empty()); assertEquals(test.findCurveDefinition(CurveName.of("Test")), Optional.of(CURVE_DEFN1)); assertEquals(test.findCurveDefinition(CurveName.of("Test2")), Optional.empty()); assertEquals(test.findCurveDefinition(CurveName.of("Rubbish")), Optional.empty()); ImmutableMap<CurveName, SeasonalityDefinition> seasonMap = test.getSeasonalityDefinitions(); assertTrue(seasonMap.size() == 1); assertEquals(seasonMap.get(CURVE_NAME_I), SEASONALITY_ADDITIVE_DEF); } public void test_builder3() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addDiscountCurve(CURVE_NAME1, GBP) .addForwardCurve(CURVE_NAME1, GBP_SONIA) .addForwardCurve(CURVE_NAME1, GBP_LIBOR_1W) .addForwardCurve(CURVE_NAME2, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); assertEquals(test.getName(), CurveGroupName.of("Test")); assertEquals(test.getEntries(), ImmutableList.of(ENTRY1, ENTRY2)); assertEquals(test.findEntry(CurveName.of("Test")), Optional.of(ENTRY1)); assertEquals(test.findEntry(CurveName.of("Test2")), Optional.of(ENTRY2)); assertEquals(test.findEntry(CurveName.of("Rubbish")), Optional.empty()); } public void test_builder4() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_NAME1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); assertEquals(test.getName(), CurveGroupName.of("Test")); assertEquals(test.getEntries(), ImmutableList.of(ENTRY3)); assertEquals(test.findEntry(CurveName.of("Test")), Optional.of(ENTRY3)); assertEquals(test.findEntry(CurveName.of("Test2")), Optional.empty()); assertEquals(test.findEntry(CurveName.of("Rubbish")), Optional.empty()); } public void test_missingEntries() { assertThrowsIllegalArg(() -> CurveGroupDefinition.of( CurveGroupName.of("group"), ImmutableList.of(ENTRY1), ImmutableList.of(CURVE_DEFN1, CURVE_DEFN2)), "An entry must be provided .* \\[Test2\\]"); } //------------------------------------------------------------------------- public void test_filtered() { DummyFraCurveNode node1 = DummyFraCurveNode.of(Period.ofDays(5), GBP_LIBOR_1M, GBP_LIBOR_1M_ID); DummyFraCurveNode node2 = DummyFraCurveNode.of(Period.ofDays(10), GBP_LIBOR_1M, GBP_LIBOR_1M_ID); DummyFraCurveNode node3 = DummyFraCurveNode.of(Period.ofDays(11), GBP_LIBOR_1M, GBP_LIBOR_1M_ID, DROP_THIS_2D); ImmutableList<DummyFraCurveNode> nodes = ImmutableList.of(node1, node2, node3); LocalDate valuationDate = date(2015, 6, 30); InterpolatedNodalCurveDefinition curveDefn = InterpolatedNodalCurveDefinition.builder() .name(CURVE_NAME1) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.ZERO_RATE) .dayCount(ACT_365F) .nodes(nodes) .interpolator(CurveInterpolators.LINEAR) .extrapolatorLeft(CurveExtrapolators.FLAT) .extrapolatorRight(CurveExtrapolators.FLAT) .build(); CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(curveDefn, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); CurveGroupDefinition expected = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(curveDefn.filtered(valuationDate, REF_DATA), GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); assertEquals(test.filtered(valuationDate, REF_DATA), expected); } //------------------------------------------------------------------------- public void test_metadata() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_DEFN1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); LocalDate valuationDate = date(2015, 6, 30); CurveMetadata meta = CURVE_DEFN1.metadata(valuationDate, REF_DATA); assertEquals(test.metadata(valuationDate, REF_DATA), ImmutableList.of(meta)); } //------------------------------------------------------------------------- public void test_tradesInitialGuesses() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_DEFN1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .build(); MarketData marketData = ImmutableMarketData.of( date(2015, 6, 30), ImmutableMap.of(GBP_LIBOR_1M_ID, 0.5d, GBP_LIBOR_3M_ID, 1.5d)); Trade trade1 = NODE1.trade(1d, marketData, REF_DATA); Trade trade2 = NODE2.trade(1d, marketData, REF_DATA); assertEquals(test.getTotalParameterCount(), 2); assertEquals(test.resolvedTrades(marketData, REF_DATA), ImmutableList.of(trade1, trade2)); assertEquals(test.initialGuesses(marketData), ImmutableList.of(0.5d, 1.5d)); } //------------------------------------------------------------------------- public void test_bind() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_DEFN1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .addForwardCurve(CURVE_DEFN_I, GB_RPI) .addSeasonality(CURVE_NAME_I, SEASONALITY_ADDITIVE_DEF) .build(); LocalDate valuationDate = LocalDate.of(2015, 11, 10); LocalDate lastFixingDate = LocalDate.of(2015, 10, 31); LocalDate otherFixingDate = LocalDate.of(2015, 9, 30); double lastFixingValue = 234.56; Map<Index, LocalDateDoubleTimeSeries> map = ImmutableMap.of(GB_RPI, LocalDateDoubleTimeSeries.builder() .put(lastFixingDate, 234.56).put(otherFixingDate, lastFixingValue - 1).build()); CurveGroupDefinition testBound = test.bindTimeSeries(valuationDate, map); List<NodalCurveDefinition> list = testBound.getCurveDefinitions(); assertEquals(list.size(), 2); assertTrue(list.get(0) instanceof InterpolatedNodalCurveDefinition); assertTrue(list.get(1) instanceof InflationNodalCurveDefinition); InflationNodalCurveDefinition seasonDef = (InflationNodalCurveDefinition) list.get(1); assertEquals(seasonDef.getCurveWithoutFixingDefinition(), CURVE_DEFN_I); assertEquals(seasonDef.getLastFixingMonth(), YearMonth.from(lastFixingDate)); assertEquals(seasonDef.getLastFixingValue(), lastFixingValue); assertEquals(seasonDef.getName(), CURVE_NAME_I); assertEquals(seasonDef.getSeasonalityDefinition(), SEASONALITY_ADDITIVE_DEF); assertEquals(seasonDef.getYValueType(), ValueType.PRICE_INDEX); } public void test_bind_after_last_fixing() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_DEFN1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .addForwardCurve(CURVE_DEFN_I, GB_RPI) .addSeasonality(CURVE_NAME_I, SEASONALITY_ADDITIVE_DEF) .build(); LocalDate valuationDate = LocalDate.of(2015, 10, 15); LocalDate lastFixingDate = LocalDate.of(2015, 10, 31); LocalDate otherFixingDate = LocalDate.of(2015, 9, 30); LocalDate other2FixingDate = LocalDate.of(2015, 8, 31); double lastFixingValue = 234.56; Map<Index, LocalDateDoubleTimeSeries> map = ImmutableMap.of(GB_RPI, LocalDateDoubleTimeSeries.builder() .put(lastFixingDate, lastFixingValue).put(otherFixingDate, lastFixingValue - 1.0) .put(other2FixingDate, lastFixingValue - 2.0).build()); CurveGroupDefinition testBound = test.bindTimeSeries(valuationDate, map); List<NodalCurveDefinition> list = testBound.getCurveDefinitions(); assertEquals(list.size(), 2); assertTrue(list.get(0) instanceof InterpolatedNodalCurveDefinition); assertTrue(list.get(1) instanceof InflationNodalCurveDefinition); InflationNodalCurveDefinition seasonDef = (InflationNodalCurveDefinition) list.get(1); assertEquals(seasonDef.getCurveWithoutFixingDefinition(), CURVE_DEFN_I); assertEquals(seasonDef.getLastFixingMonth(), YearMonth.from(otherFixingDate)); assertEquals(seasonDef.getLastFixingValue(), lastFixingValue - 1.0); assertEquals(seasonDef.getName(), CURVE_NAME_I); assertEquals(seasonDef.getSeasonalityDefinition(), SEASONALITY_ADDITIVE_DEF); assertEquals(seasonDef.getYValueType(), ValueType.PRICE_INDEX); } public void test_bind_no_seasonality() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addCurve(CURVE_DEFN1, GBP, GBP_LIBOR_1M, GBP_LIBOR_3M) .addForwardCurve(CURVE_DEFN_I, GB_RPI) .build(); LocalDate valuationDate = LocalDate.of(2015, 11, 10); LocalDate lastFixingDate = LocalDate.of(2015, 10, 31); LocalDate otherFixingDate = LocalDate.of(2015, 9, 30); double lastFixingValue = 234.56; Map<Index, LocalDateDoubleTimeSeries> map = ImmutableMap.of(GB_RPI, LocalDateDoubleTimeSeries.builder() .put(lastFixingDate, 234.56).put(otherFixingDate, lastFixingValue - 1).build()); CurveGroupDefinition testBound = test.bindTimeSeries(valuationDate, map); List<NodalCurveDefinition> list = testBound.getCurveDefinitions(); assertEquals(list.size(), 2); assertTrue(list.get(0) instanceof InterpolatedNodalCurveDefinition); assertTrue(list.get(1) instanceof InflationNodalCurveDefinition); InflationNodalCurveDefinition seasonDef = (InflationNodalCurveDefinition) list.get(1); assertEquals(seasonDef.getCurveWithoutFixingDefinition(), CURVE_DEFN_I); assertEquals(seasonDef.getLastFixingMonth(), YearMonth.from(lastFixingDate)); assertEquals(seasonDef.getLastFixingValue(), lastFixingValue); assertEquals(seasonDef.getName(), CURVE_NAME_I); assertEquals(seasonDef.getYValueType(), ValueType.PRICE_INDEX); // Check the default assertTrue(seasonDef.getSeasonalityDefinition().getSeasonalityMonthOnMonth() .equalWithTolerance(DoubleArray.filled(12, 1d), 1.0E-10)); assertEquals(seasonDef.getSeasonalityDefinition().getAdjustmentType(), ShiftType.SCALED); } //------------------------------------------------------------------------- public void coverage() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addDiscountCurve(CURVE_DEFN1, GBP) .build(); coverImmutableBean(test); CurveGroupDefinition test2 = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test2")) .addForwardCurve(CURVE_DEFN2, GBP_LIBOR_1M) .build(); coverBeanEquals(test, test2); } public void test_serialization() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addDiscountCurve(CURVE_DEFN1, GBP) .build(); assertSerialization(test); } public void test_withName() { CurveGroupDefinition test = CurveGroupDefinition.builder() .name(CurveGroupName.of("Test")) .addDiscountCurve(CURVE_DEFN1, GBP) .build(); CurveGroupDefinition expected = CurveGroupDefinition.builder() .name(CurveGroupName.of("NewName")) .addDiscountCurve(CURVE_DEFN1, GBP) .build(); CurveGroupDefinition withNewName = test.withName(CurveGroupName.of("NewName")); assertEquals(withNewName, expected); } }