/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.provider.calculator.discounting; import static org.testng.AssertJUnit.assertEquals; import java.util.LinkedHashMap; import java.util.Map; import org.testng.annotations.Test; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.ShiftType; import com.opengamma.analytics.financial.instrument.annuity.AnnuityCouponIborSpreadDefinition; import com.opengamma.analytics.financial.instrument.index.GeneratorSwapFixedIbor; import com.opengamma.analytics.financial.instrument.index.GeneratorSwapFixedIborMaster; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexON; import com.opengamma.analytics.financial.instrument.swap.SwapFixedIborDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapIborIborDefinition; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitor; import com.opengamma.analytics.financial.interestrate.swap.derivative.Swap; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurveUtils; import com.opengamma.analytics.financial.provider.description.MulticurveProviderDiscountDataSets; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface; import com.opengamma.analytics.util.amount.ReferenceAmount; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.util.money.Currency; import com.opengamma.util.money.MultipleCurrencyAmount; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.DateUtils; import com.opengamma.util.tuple.Pair; /** * Tests PV01 and gamma PV01 calculations. */ @Test(groups = TestGroup.UNIT) public class PV01CurveParametersCalculatorTest { /** Provides discounting and forward Ibor curves */ private static final MulticurveProviderDiscount MULTICURVES = MulticurveProviderDiscountDataSets.createMulticurveEurUsd(); /** Provides discounting and forward Ibor curves that have been shifted by 1bp */ private static final MulticurveProviderDiscount SHIFTED; /** Gets the Ibor indices */ private static final IborIndex[] INDEX_LIST = MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd(); /** The 3m USD Libor index */ private static final IborIndex USDLIBOR3M = INDEX_LIST[2]; /** The 6m USD Libor index */ private static final IborIndex USDLIBOR6M = INDEX_LIST[3]; /** NYC calendar */ private static final Calendar NYC = MulticurveProviderDiscountDataSets.getUSDCalendar(); /** Generates fixed / ibor swaps */ private static final GeneratorSwapFixedIborMaster GENERATOR_SWAP_MASTER = GeneratorSwapFixedIborMaster.getInstance(); /** Generates standard USD swaps */ private static final GeneratorSwapFixedIbor USD6MLIBOR3M = GENERATOR_SWAP_MASTER.getGenerator("USD6MLIBOR3M", NYC); /** The swap tenor */ private static final Period SWAP_TENOR = Period.ofYears(10); /** The settlement date */ private static final ZonedDateTime SETTLEMENT_DATE = DateUtils.getUTCDate(2012, 5, 17); /** The reference date */ private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2011, 5, 17); /** The notional */ private static final double NOTIONAL = 100000000; /** The fixed rate */ private static final double FIXED_RATE = 0.025; /** A fixed / Libor swap */ private static final Swap<?, ?> SWAP_FIXED_IBOR = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, FIXED_RATE, true).toDerivative(REFERENCE_DATE); /** A 3m Libor / 6m Libor swap */ private static final Swap<?, ?> SWAP_IBORSPREAD_IBORSPREAD_DEFINITION = new SwapIborIborDefinition(AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR3M, 0.001, true, NYC), AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR6M, 0.001, false, NYC)).toDerivative(REFERENCE_DATE); /** The PV calculator */ private static final InstrumentDerivativeVisitor<ParameterProviderInterface, MultipleCurrencyAmount> PV = PresentValueDiscountingCalculator.getInstance(); /** The PV01 calculator */ private static final PV01CurveParametersCalculator<ParameterProviderInterface> PV01 = new PV01CurveParametersCalculator<>(PresentValueCurveSensitivityDiscountingCalculator.getInstance()); /** The gamma PV01 calculator */ private static final GammaPV01CurveParametersCalculator<ParameterProviderInterface> GAMMA_PV01 = new GammaPV01CurveParametersCalculator<>(PresentValueCurveSensitivityDiscountingCalculator.getInstance()); /** One basis point */ private static final double BP = 0.0001; /** Relative accuracy for calculations */ private static final double EPS_FIRST = 1e-3; static { final Map<Currency, YieldAndDiscountCurve> discountCurves = new LinkedHashMap<>(); for (final Map.Entry<Currency, YieldAndDiscountCurve> entry : MULTICURVES.getDiscountingCurves().entrySet()) { discountCurves.put(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP, ShiftType.ABSOLUTE)); } final Map<IborIndex, YieldAndDiscountCurve> iborCurves = new LinkedHashMap<>(); for (final Map.Entry<IborIndex, YieldAndDiscountCurve> entry : MULTICURVES.getForwardIborCurves().entrySet()) { iborCurves.put(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP, ShiftType.ABSOLUTE)); } final Map<IndexON, YieldAndDiscountCurve> overnightCurves = new LinkedHashMap<>(); for (final Map.Entry<IndexON, YieldAndDiscountCurve> entry : MULTICURVES.getForwardONCurves().entrySet()) { overnightCurves.put(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP, ShiftType.ABSOLUTE)); } SHIFTED = new MulticurveProviderDiscount(discountCurves, iborCurves, overnightCurves, MULTICURVES.getFxRates()); } /** * Tests that the correct exception is thrown when the curve sensitivity calculator is null */ @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurveSensitivityCalculator1() { new PV01CurveParametersCalculator<>(null); } /** * Tests that the correct exception is thrown when the curve sensitivity calculator is null */ @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurveSensitivityCalculator2() { new GammaPV01CurveParametersCalculator<>(null); } /** * Tests that the correct exception is thrown when the instrument passed in is null */ @Test(expectedExceptions = IllegalArgumentException.class) public void testNullInstrument1() { PV01.visit(null, MULTICURVES); } /** * Tests that the correct exception is thrown when the instrument passed in is null */ @Test(expectedExceptions = IllegalArgumentException.class) public void testNullInstrument2() { GAMMA_PV01.visit(null, MULTICURVES); } /** * Tests that the correct exception is thrown when the curve data passed in is null */ @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData1() { PV01.visit(SWAP_FIXED_IBOR, null); } /** * Tests that the correct exception is thrown when the curve data passed in is null */ @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData2() { GAMMA_PV01.visit(SWAP_FIXED_IBOR, null); } /** * Tests that the correct exception is thrown with the wrong visit() method is used */ @Test(expectedExceptions = UnsupportedOperationException.class) public void testVisit1() { PV01.visit(SWAP_FIXED_IBOR); } /** * Tests that the correct exception is thrown with the wrong visit() method is used */ @Test(expectedExceptions = UnsupportedOperationException.class) public void testVisit2() { GAMMA_PV01.visit(SWAP_FIXED_IBOR); } /** * Tests that total PV01 (i.e. PV01 and gamma PV01) for swaps is a good approximation to the * change in PV when all curves to which the instruments are sensitive are shifted by +1bp */ @Test public void testApproximation() { ReferenceAmount<Pair<String, Currency>> pv01s = SWAP_FIXED_IBOR.accept(PV01, MULTICURVES); double pv01 = 0; for (final Map.Entry<Pair<String, Currency>, Double> entry : pv01s.getMap().entrySet()) { if (entry.getKey().getSecond().equals(Currency.USD)) { pv01 += entry.getValue(); } } ReferenceAmount<Pair<String, Currency>> pv01Ups = SWAP_FIXED_IBOR.accept(PV01, SHIFTED); double pv01Up = 0; for (final Map.Entry<Pair<String, Currency>, Double> entry : pv01Ups.getMap().entrySet()) { if (entry.getKey().getSecond().equals(Currency.USD)) { pv01Up += entry.getValue(); } } double gammaPV01 = SWAP_FIXED_IBOR.accept(GAMMA_PV01, MULTICURVES); MultipleCurrencyAmount pv = SWAP_FIXED_IBOR.accept(PV, MULTICURVES); MultipleCurrencyAmount pvUp = SWAP_FIXED_IBOR.accept(PV, SHIFTED); double expectedPV01 = pvUp.getAmount(Currency.USD) - pv.getAmount(Currency.USD); assertEquals(0, (expectedPV01 - pv01) / expectedPV01, EPS_FIRST); double expectedGammaPV01 = (pv01Up - pv01) / BP; assertEquals(0, (expectedGammaPV01 - gammaPV01) / expectedGammaPV01, EPS_FIRST); pv01s = SWAP_IBORSPREAD_IBORSPREAD_DEFINITION.accept(PV01, MULTICURVES); pv01 = 0; for (final Map.Entry<Pair<String, Currency>, Double> entry : pv01s.getMap().entrySet()) { if (entry.getKey().getSecond().equals(Currency.USD)) { pv01 += entry.getValue(); } } pv01Ups = SWAP_IBORSPREAD_IBORSPREAD_DEFINITION.accept(PV01, SHIFTED); pv01Up = 0; for (final Map.Entry<Pair<String, Currency>, Double> entry : pv01Ups.getMap().entrySet()) { if (entry.getKey().getSecond().equals(Currency.USD)) { pv01Up += entry.getValue(); } } gammaPV01 = SWAP_IBORSPREAD_IBORSPREAD_DEFINITION.accept(GAMMA_PV01, MULTICURVES); pv = SWAP_IBORSPREAD_IBORSPREAD_DEFINITION.accept(PV, MULTICURVES); pvUp = SWAP_IBORSPREAD_IBORSPREAD_DEFINITION.accept(PV, SHIFTED); expectedPV01 = pvUp.getAmount(Currency.USD) - pv.getAmount(Currency.USD); assertEquals(0, (expectedPV01 - pv01) / expectedPV01, EPS_FIRST); expectedGammaPV01 = (pv01Up - pv01) / BP; assertEquals(0, (expectedGammaPV01 - gammaPV01) / expectedGammaPV01, EPS_FIRST); } }