/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.curve; import static com.opengamma.strata.basics.currency.Currency.USD; import static com.opengamma.strata.basics.date.BusinessDayConventions.FOLLOWING; import static com.opengamma.strata.basics.date.DayCounts.ACT_360; import static com.opengamma.strata.basics.date.DayCounts.ACT_365F; import static com.opengamma.strata.basics.date.HolidayCalendarIds.USNY; import static com.opengamma.strata.basics.index.IborIndices.USD_LIBOR_3M; import static com.opengamma.strata.basics.index.OvernightIndices.USD_FED_FUND; import static com.opengamma.strata.basics.index.PriceIndices.US_CPI_U; import static com.opengamma.strata.product.swap.type.FixedOvernightSwapConventions.USD_FIXED_1Y_FED_FUND_OIS; import static org.testng.Assert.assertEquals; import java.time.LocalDate; import java.time.Period; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.testng.annotations.Test; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.StandardId; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.currency.CurrencyAmount; import com.opengamma.strata.basics.currency.MultiCurrencyAmount; import com.opengamma.strata.basics.date.BusinessDayAdjustment; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.date.DaysAdjustment; import com.opengamma.strata.basics.date.Tenor; import com.opengamma.strata.basics.index.Index; import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries; import com.opengamma.strata.data.ImmutableMarketData; import com.opengamma.strata.data.ImmutableMarketDataBuilder; import com.opengamma.strata.market.ValueType; import com.opengamma.strata.market.curve.CurveGroupDefinition; import com.opengamma.strata.market.curve.CurveGroupName; import com.opengamma.strata.market.curve.CurveName; import com.opengamma.strata.market.curve.CurveNode; import com.opengamma.strata.market.curve.CurveNodeDate; import com.opengamma.strata.market.curve.InterpolatedNodalCurveDefinition; import com.opengamma.strata.market.curve.interpolator.CurveExtrapolator; import com.opengamma.strata.market.curve.interpolator.CurveExtrapolators; import com.opengamma.strata.market.curve.interpolator.CurveInterpolator; import com.opengamma.strata.market.curve.interpolator.CurveInterpolators; import com.opengamma.strata.market.curve.node.FixedInflationSwapCurveNode; import com.opengamma.strata.market.curve.node.FixedOvernightSwapCurveNode; import com.opengamma.strata.market.curve.node.TermDepositCurveNode; import com.opengamma.strata.market.observable.IndexQuoteId; import com.opengamma.strata.market.observable.QuoteId; import com.opengamma.strata.pricer.deposit.DiscountingTermDepositProductPricer; import com.opengamma.strata.pricer.rate.RatesProvider; import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer; import com.opengamma.strata.product.ResolvedTrade; import com.opengamma.strata.product.deposit.ResolvedTermDepositTrade; import com.opengamma.strata.product.deposit.type.ImmutableTermDepositConvention; import com.opengamma.strata.product.deposit.type.TermDepositConvention; import com.opengamma.strata.product.deposit.type.TermDepositTemplate; import com.opengamma.strata.product.swap.ResolvedSwapTrade; import com.opengamma.strata.product.swap.type.FixedInflationSwapConventions; import com.opengamma.strata.product.swap.type.FixedInflationSwapTemplate; import com.opengamma.strata.product.swap.type.FixedOvernightSwapTemplate; /** * Test for curve calibration with 2 curves in USD. * One curve is Discounting and Fed Fund forward and the other one is USD CPI price index. */ @Test public class CalibrationInflationUsdTest { private static final LocalDate VAL_DATE = LocalDate.of(2015, 7, 21); private static final CurveInterpolator INTERPOLATOR_LINEAR = CurveInterpolators.LINEAR; private static final CurveExtrapolator EXTRAPOLATOR_FLAT = CurveExtrapolators.FLAT; private static final CurveInterpolator INTERPOLATOR_LOGLINEAR = CurveInterpolators.LOG_LINEAR; private static final CurveExtrapolator EXTRAPOLATOR_EXP = CurveExtrapolators.EXPONENTIAL; private static final DayCount CURVE_DC = ACT_365F; // reference data private static final ReferenceData REF_DATA = ReferenceData.standard(); private static final String SCHEME = "CALIBRATION"; /** Curve names */ private static final String DSCON_NAME = "USD-DSCON-OIS"; private static final CurveName DSCON_CURVE_NAME = CurveName.of(DSCON_NAME); private static final String CPI_NAME = "USD-CPI-ZC"; private static final CurveName CPI_CURVE_NAME = CurveName.of(CPI_NAME); /** Curves associations to currencies and indices. */ private static final Map<CurveName, Currency> DSC_NAMES = new HashMap<>(); private static final Map<CurveName, Set<Index>> IDX_NAMES = new HashMap<>(); private static final LocalDateDoubleTimeSeries TS_USD_CPI = LocalDateDoubleTimeSeries.builder().put(LocalDate.of(2015, 6, 30), 123.4).build(); static { DSC_NAMES.put(DSCON_CURVE_NAME, USD); Set<Index> usdFedFundSet = new HashSet<>(); usdFedFundSet.add(USD_FED_FUND); IDX_NAMES.put(DSCON_CURVE_NAME, usdFedFundSet); Set<Index> usdLibor3Set = new HashSet<>(); usdLibor3Set.add(USD_LIBOR_3M); IDX_NAMES.put(CPI_CURVE_NAME, usdLibor3Set); } /** Data for USD-DSCON curve */ /* Market values */ private static final double[] DSC_MARKET_QUOTES = new double[] { 0.0005, 0.0005, 0.00072000, 0.00082000, 0.00093000, 0.00090000, 0.00105000, 0.00118500, 0.00318650, 0.00318650, 0.00704000, 0.01121500, 0.01515000, 0.01845500, 0.02111000, 0.02332000, 0.02513500, 0.02668500}; private static final int DSC_NB_NODES = DSC_MARKET_QUOTES.length; private static final String[] DSC_ID_VALUE = new String[] { "USD-ON", "USD-TN", "USD-OIS-1M", "USD-OIS-2M", "USD-OIS-3M", "USD-OIS-6M", "USD-OIS-9M", "USD-OIS-1Y", "USD-OIS-18M", "USD-OIS-2Y", "USD-OIS-3Y", "USD-OIS-4Y", "USD-OIS-5Y", "USD-OIS-6Y", "USD-OIS-7Y", "USD-OIS-8Y", "USD-OIS-9Y", "USD-OIS-10Y"}; /* Nodes */ private static final CurveNode[] DSC_NODES = new CurveNode[DSC_NB_NODES]; /* Tenors */ private static final int[] DSC_DEPO_OFFSET = new int[] {0, 1}; private static final int DSC_NB_DEPO_NODES = DSC_DEPO_OFFSET.length; private static final Period[] DSC_OIS_TENORS = new Period[] { Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5), Period.ofYears(6), Period.ofYears(7), Period.ofYears(8), Period.ofYears(9), Period.ofYears(10)}; private static final int DSC_NB_OIS_NODES = DSC_OIS_TENORS.length; static { for (int i = 0; i < DSC_NB_DEPO_NODES; i++) { BusinessDayAdjustment bda = BusinessDayAdjustment.of(FOLLOWING, USNY); TermDepositConvention convention = ImmutableTermDepositConvention.of( "USD-Dep", USD, bda, ACT_360, DaysAdjustment.ofBusinessDays(DSC_DEPO_OFFSET[i], USNY)); DSC_NODES[i] = TermDepositCurveNode.of(TermDepositTemplate.of(Period.ofDays(1), convention), QuoteId.of(StandardId.of(SCHEME, DSC_ID_VALUE[i]))); } for (int i = 0; i < DSC_NB_OIS_NODES; i++) { DSC_NODES[DSC_NB_DEPO_NODES + i] = FixedOvernightSwapCurveNode.of( FixedOvernightSwapTemplate.of(Period.ZERO, Tenor.of(DSC_OIS_TENORS[i]), USD_FIXED_1Y_FED_FUND_OIS), QuoteId.of(StandardId.of(SCHEME, DSC_ID_VALUE[DSC_NB_DEPO_NODES + i]))); } } /** Data for USD-CPI curve */ /* Market values */ private static final double[] CPI_MARKET_QUOTES = new double[] { 0.0200, 0.0200, 0.0200, 0.0200, 0.0200}; private static final int CPI_NB_NODES = CPI_MARKET_QUOTES.length; private static final String[] CPI_ID_VALUE = new String[] { "USD-CPI-1Y", "USD-CPI-2Y", "USD-CPI-3Y", "USD-CPI-4Y", "USD-CPI-5Y"}; /* Nodes */ private static final CurveNode[] CPI_NODES = new CurveNode[CPI_NB_NODES]; /* Tenors */ private static final Period[] CPI_TENORS = new Period[] { Period.ofYears(1), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5)}; static { for (int i = 0; i < CPI_NB_NODES; i++) { CPI_NODES[i] = FixedInflationSwapCurveNode.builder() .template(FixedInflationSwapTemplate.of(Tenor.of(CPI_TENORS[i]), FixedInflationSwapConventions.USD_FIXED_ZC_US_CPI)) .rateId(QuoteId.of(StandardId.of(SCHEME, CPI_ID_VALUE[i]))) .date(CurveNodeDate.LAST_FIXING) .build(); } } /** All quotes for the curve calibration */ private static final ImmutableMarketData ALL_QUOTES; static { ImmutableMarketDataBuilder builder = ImmutableMarketData.builder(VAL_DATE); for (int i = 0; i < DSC_NB_NODES; i++) { builder.addValue(QuoteId.of(StandardId.of(SCHEME, DSC_ID_VALUE[i])), DSC_MARKET_QUOTES[i]); } for (int i = 0; i < CPI_NB_NODES; i++) { builder.addValue(QuoteId.of(StandardId.of(SCHEME, CPI_ID_VALUE[i])), CPI_MARKET_QUOTES[i]); } builder.addTimeSeries(IndexQuoteId.of(US_CPI_U), TS_USD_CPI); ALL_QUOTES = builder.build(); } /** All nodes by groups. */ private static final List<List<CurveNode[]>> CURVES_NODES = new ArrayList<>(); static { List<CurveNode[]> groupDsc = new ArrayList<>(); groupDsc.add(DSC_NODES); CURVES_NODES.add(groupDsc); List<CurveNode[]> groupCpi = new ArrayList<>(); groupCpi.add(CPI_NODES); CURVES_NODES.add(groupCpi); } private static final DiscountingSwapProductPricer SWAP_PRICER = DiscountingSwapProductPricer.DEFAULT; private static final DiscountingTermDepositProductPricer DEPO_PRICER = DiscountingTermDepositProductPricer.DEFAULT; private static final CurveCalibrator CALIBRATOR = CurveCalibrator.of(1e-9, 1e-9, 100); // Constants private static final double TOLERANCE_PV = 1.0E-6; private static final CurveGroupName CURVE_GROUP_NAME = CurveGroupName.of("USD-DSCON-LIBOR3M"); private static final InterpolatedNodalCurveDefinition DSC_CURVE_DEFN = InterpolatedNodalCurveDefinition.builder() .name(DSCON_CURVE_NAME) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.ZERO_RATE) .dayCount(CURVE_DC) .interpolator(INTERPOLATOR_LINEAR) .extrapolatorLeft(EXTRAPOLATOR_FLAT) .extrapolatorRight(EXTRAPOLATOR_FLAT) .nodes(DSC_NODES).build(); private static final InterpolatedNodalCurveDefinition CPI_CURVE_UNDER_DEFN = InterpolatedNodalCurveDefinition.builder() .name(CPI_CURVE_NAME) .xValueType(ValueType.MONTHS) .yValueType(ValueType.PRICE_INDEX) .dayCount(CURVE_DC) .interpolator(INTERPOLATOR_LOGLINEAR) .extrapolatorLeft(EXTRAPOLATOR_FLAT) .extrapolatorRight(EXTRAPOLATOR_EXP) .nodes(CPI_NODES).build(); private static final CurveGroupDefinition CURVE_GROUP_CONFIG = CurveGroupDefinition.builder() .name(CURVE_GROUP_NAME) .addCurve(DSC_CURVE_DEFN, USD, USD_FED_FUND) .addForwardCurve(CPI_CURVE_UNDER_DEFN, US_CPI_U).build(); //------------------------------------------------------------------------- public void calibration_present_value_oneGroup() { RatesProvider result = CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, ALL_QUOTES, REF_DATA); assertPresentValue(result); } private void assertPresentValue(RatesProvider result) { // Test PV Dsc CurveNode[] dscNodes = CURVES_NODES.get(0).get(0); List<ResolvedTrade> dscTrades = new ArrayList<>(); for (int i = 0; i < dscNodes.length; i++) { dscTrades.add(dscNodes[i].resolvedTrade(1d, ALL_QUOTES, REF_DATA)); } // Depo for (int i = 0; i < DSC_NB_DEPO_NODES; i++) { CurrencyAmount pvIrs = DEPO_PRICER.presentValue( ((ResolvedTermDepositTrade) dscTrades.get(i)).getProduct(), result); assertEquals(pvIrs.getAmount(), 0.0, TOLERANCE_PV); } // OIS for (int i = 0; i < DSC_NB_OIS_NODES; i++) { MultiCurrencyAmount pvIrs = SWAP_PRICER.presentValue( ((ResolvedSwapTrade) dscTrades.get(DSC_NB_DEPO_NODES + i)).getProduct(), result); assertEquals(pvIrs.getAmount(USD).getAmount(), 0.0, TOLERANCE_PV); } // Test PV Infaltion swaps CurveNode[] cpiNodes = CURVES_NODES.get(1).get(0); List<ResolvedTrade> cpiTrades = new ArrayList<>(); for (int i = 0; i < cpiNodes.length; i++) { cpiTrades.add(cpiNodes[i].resolvedTrade(1d, ALL_QUOTES, REF_DATA)); } // ZC swaps for (int i = 0; i < CPI_NB_NODES; i++) { MultiCurrencyAmount pvInfl = SWAP_PRICER.presentValue( ((ResolvedSwapTrade) cpiTrades.get(i)).getProduct(), result); assertEquals(pvInfl.getAmount(USD).getAmount(), 0.0, TOLERANCE_PV); } } //------------------------------------------------------------------------- @SuppressWarnings("unused") @Test(enabled = false) public void performance() { long startTime, endTime; int nbTests = 100; int nbRep = 3; int count = 0; for (int i = 0; i < nbRep; i++) { startTime = System.currentTimeMillis(); for (int looprep = 0; looprep < nbTests; looprep++) { RatesProvider result = CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, ALL_QUOTES, REF_DATA); count += result.getValuationDate().getDayOfMonth(); } endTime = System.currentTimeMillis(); System.out.println("Performance: " + nbTests + " calibrations for 2 curves with 35 nodes in " + (endTime - startTime) + " ms."); } System.out.println("Avoiding hotspot: " + count); // Previous run: 275 ms for 100 calibrations (2 curves simultaneous - 35 nodes) } }