/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.interestrate.swap.provider; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.instrument.annuity.AnnuityCouponIborDefinition; 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.swap.SwapDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapFixedIborDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapIborIborDefinition; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.analytics.financial.interestrate.annuity.derivative.Annuity; import com.opengamma.analytics.financial.interestrate.datasets.StandardDataSetsMulticurveUSD; import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon; import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment; import com.opengamma.analytics.financial.interestrate.swap.derivative.Swap; import com.opengamma.analytics.financial.interestrate.swap.derivative.SwapFixedCoupon; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.provider.calculator.discounting.CrossGammaSingleCurveCalculator; import com.opengamma.analytics.financial.provider.calculator.discounting.PV01CurveParametersCalculator; import com.opengamma.analytics.financial.provider.calculator.discounting.ParRateDiscountingCalculator; import com.opengamma.analytics.financial.provider.calculator.discounting.ParSpreadMarketQuoteDiscountingCalculator; import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueCurveSensitivityDiscountingCalculator; import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingCalculator; import com.opengamma.analytics.financial.provider.calculator.generic.MarketQuoteSensitivityBlockCalculator; import com.opengamma.analytics.financial.provider.calculator.generic.TodayPaymentCalculator; import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle; 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.MulticurveProviderInterface; import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterAbstractCalculator; import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterCalculator; import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityUnderlyingParameterCalculator; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.analytics.util.amount.ReferenceAmount; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.timeseries.precise.zdt.ImmutableZonedDateTimeDoubleTimeSeries; import com.opengamma.timeseries.precise.zdt.ZonedDateTimeDoubleTimeSeries; import com.opengamma.util.ArgumentChecker; 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 related to swap calculators. */ @Test(groups = TestGroup.UNIT) public class SwapCalculatorTest { private static final MulticurveProviderDiscount MULTICURVES = MulticurveProviderDiscountDataSets.createMulticurveEurUsd(); private static final IborIndex[] INDEX_LIST = MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd(); private static final IborIndex USDLIBOR3M = INDEX_LIST[2]; private static final IborIndex USDLIBOR6M = INDEX_LIST[3]; private static final Currency USD = USDLIBOR3M.getCurrency(); private static final Calendar NYC = MulticurveProviderDiscountDataSets.getUSDCalendar(); private static final GeneratorSwapFixedIborMaster GENERATOR_SWAP_MASTER = GeneratorSwapFixedIborMaster.getInstance(); /** IRS Fixed v LIBOR3M */ private static final GeneratorSwapFixedIbor USD6MLIBOR3M = GENERATOR_SWAP_MASTER.getGenerator("USD6MLIBOR3M", NYC); private static final Period SWAP_TENOR = Period.ofYears(10); private static final ZonedDateTime SETTLEMENT_DATE = DateUtils.getUTCDate(2012, 5, 17); private static final double NOTIONAL = 100000000; //100m private static final double RATE_FIXED = 0.025; private static final SwapFixedIborDefinition SWAP_FIXED_IBOR_DEFINITION = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, RATE_FIXED, true); private static final Period SWAP_TENOR_2 = Period.ofYears(15); // Tenor more than 10Y to be after the last node on the discounting curve (10Y). private static final SwapFixedIborDefinition SWAP_FIXED_IBOR_2_DEFINITION = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR_2, USD6MLIBOR3M, NOTIONAL, RATE_FIXED, true); /** Basis Swap LIBOR3M v LIBOR6M */ private static final double SPREAD3 = 0.0020; private static final double SPREAD6 = 0.0005; private static final SwapIborIborDefinition SWAP_IBORSPREAD_IBORSPREAD_DEFINITION = new SwapIborIborDefinition(AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR3M, SPREAD3, true, NYC), AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR6M, SPREAD6, false, NYC)); private static final SwapDefinition SWAP_IBOR_IBORSPREAD_DEFINITION = new SwapDefinition(AnnuityCouponIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR3M, true, NYC), AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR6M, SPREAD6, false, NYC)); private static final ZonedDateTimeDoubleTimeSeries FIXING_TS_3 = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] {DateUtils.getUTCDate(2012, 5, 10), DateUtils.getUTCDate(2012, 5, 14), DateUtils.getUTCDate(2012, 5, 15), DateUtils.getUTCDate(2012, 5, 16), DateUtils.getUTCDate(2012, 8, 15), DateUtils.getUTCDate(2012, 11, 15) }, new double[] {0.0080, 0.0090, 0.0100, 0.0110, 0.0140, 0.0160 }); private static final ZonedDateTimeDoubleTimeSeries FIXING_TS_6 = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC(new ZonedDateTime[] {DateUtils.getUTCDate(2012, 5, 10), DateUtils.getUTCDate(2012, 5, 15), DateUtils.getUTCDate(2012, 5, 16) }, new double[] {0.0095, 0.0120, 0.0130 }); private static final ZonedDateTimeDoubleTimeSeries[] FIXING_TS_3_6 = new ZonedDateTimeDoubleTimeSeries[] {FIXING_TS_3, FIXING_TS_6 }; /** Calculators */ private static final ParSpreadMarketQuoteDiscountingCalculator PSMQDC = ParSpreadMarketQuoteDiscountingCalculator.getInstance(); private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance(); private static final ParRateDiscountingCalculator PRDC = ParRateDiscountingCalculator.getInstance(); private static final TodayPaymentCalculator TPC = TodayPaymentCalculator.getInstance(); private static final PV01CurveParametersCalculator<ParameterProviderInterface> PV01CPC = new PV01CurveParametersCalculator<>(PresentValueCurveSensitivityDiscountingCalculator.getInstance()); private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC = PresentValueCurveSensitivityDiscountingCalculator.getInstance(); private static final ParameterSensitivityParameterCalculator<ParameterProviderInterface> PSPVC = new ParameterSensitivityParameterCalculator<>(PVCSDC); private static final ParameterSensitivityParameterAbstractCalculator<ParameterProviderInterface> PSUC = new ParameterSensitivityUnderlyingParameterCalculator<>(PVCSDC); private static final MarketQuoteSensitivityBlockCalculator<ParameterProviderInterface> MQSUBC = new MarketQuoteSensitivityBlockCalculator<>(PSUC); private static final CrossGammaSingleCurveCalculator CGC = new CrossGammaSingleCurveCalculator(PVCSDC); private static final double TOLERANCE_PV = 1.0E-2; // one cent out of 100m private static final double TOLERANCE_PV_DELTA = 1.0E+4; private static final double TOLERANCE_PV_GAMMA = 2.0E+0; private static final double TOLERANCE_PV_GAMMA_RELATIF = 5.0E-4; private static final double BP1 = 1.0E-4; // The size of the scaling: 1 basis point. private static final double SHIFT = 1.0E-4; private static final double SHIFT_2 = 1.0E-6; @Test /** * Test the market quote sensitivity in the case of curve calibrated as a spread on an existing curve. * The parameter sensitivity calculator is the ParameterSensitivityUnderlyingParameterCalculator which takes into * account the underlying curves up to one level deep. * The test is done versus a finite difference computation with full curve re-calibration. */ public void marketQuoteSensitivitySpreadCurve() { double[] mqDsc = StandardDataSetsMulticurveUSD.getMarketDataDsc1(); double[] mqFwd3 = StandardDataSetsMulticurveUSD.getMarketDataFwd31(); String nameDsc = StandardDataSetsMulticurveUSD.getDscCurveName(); String nameFwd3 = StandardDataSetsMulticurveUSD.getFwd3CurveName(); Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> multicurveSpreadPair = StandardDataSetsMulticurveUSD.getCurvesUSDOisSpreadL3(); MulticurveProviderInterface multicurveSpread = multicurveSpreadPair.getFirst(); CurveBuildingBlockBundle blockSpread = multicurveSpreadPair.getSecond(); final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_2_DEFINITION.toDerivative(referenceDate); double pv = swap.accept(PVDC, multicurveSpread).getAmount(USD); MultipleCurrencyParameterSensitivity mqs = MQSUBC.fromInstrument(swap, multicurveSpread, blockSpread); int nbNodDsc = mqDsc.length; double[] mqsDscComputed = mqs.getSensitivity(nameDsc, USD).getData(); double[] mqsDscExpected = new double[nbNodDsc]; for (int loopnodedsc = 0; loopnodedsc < nbNodDsc; loopnodedsc++) { double[] mqDscShifted = mqDsc.clone(); mqDscShifted[loopnodedsc] += SHIFT_2; MulticurveProviderDiscount multicurveShifted = StandardDataSetsMulticurveUSD.getCurvesUSDOisSpreadL3(mqDscShifted, mqFwd3).getFirst(); double pvShifted = swap.accept(PVDC, multicurveShifted).getAmount(USD); mqsDscExpected[loopnodedsc] = (pvShifted - pv) / SHIFT_2; assertEquals("ParameterSensitivityUnderlyingParameterCalculator - " + loopnodedsc, mqsDscExpected[loopnodedsc], mqsDscComputed[loopnodedsc], TOLERANCE_PV_DELTA); } int nbNodeFwd3 = mqFwd3.length; double[] mqsFwd3Computed = mqs.getSensitivity(nameFwd3, USD).getData(); double[] mqsFwd3Expected = new double[nbNodeFwd3]; for (int loopnodefwd = 0; loopnodefwd < nbNodeFwd3; loopnodefwd++) { double[] mqFwdShifted = mqFwd3.clone(); mqFwdShifted[loopnodefwd] += SHIFT_2; MulticurveProviderDiscount multicurveShifted = StandardDataSetsMulticurveUSD.getCurvesUSDOisSpreadL3(mqDsc, mqFwdShifted).getFirst(); double pvShifted = swap.accept(PVDC, multicurveShifted).getAmount(USD); mqsFwd3Expected[loopnodefwd] = (pvShifted - pv) / SHIFT_2; assertEquals("ParameterSensitivityUnderlyingParameterCalculator - " + loopnodefwd, mqsFwd3Expected[loopnodefwd], mqsFwd3Computed[loopnodefwd], TOLERANCE_PV_DELTA); } } @Test /** Tests the cross-gamma calculator vs bump and recompute. */ public void crossGamma() { MulticurveProviderDiscount singleCurve = MulticurveProviderDiscountDataSets.createSingleCurveZcUsd(); final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate); String name = singleCurve.getAllNames().iterator().next(); Currency ccy = singleCurve.getCurrencyForName(name).get(0); YieldAndDiscountCurve curve = singleCurve.getCurve(name); ArgumentChecker.isTrue(curve instanceof YieldCurve, "curve should be YieldCurve"); YieldCurve yieldCurve = (YieldCurve) curve; ArgumentChecker.isTrue(yieldCurve.getCurve() instanceof InterpolatedDoublesCurve, "Yield curve should be based on InterpolatedDoublesCurve"); InterpolatedDoublesCurve interpolatedCurve = (InterpolatedDoublesCurve) yieldCurve.getCurve(); double[] y = interpolatedCurve.getYDataAsPrimitive(); double[] x = interpolatedCurve.getXDataAsPrimitive(); int nbNode = y.length; double[][] gammaComputed = CGC.calculateCrossGamma(swap, singleCurve).getData(); double[][] gammaExpected = new double[nbNode][nbNode]; for (int i = 0; i < nbNode; i++) { for (int j = 0; j < nbNode; j++) { double[][] pv = new double[2][2]; for (int pmi = 0; pmi < 2; pmi++) { for (int pmj = 0; pmj < 2; pmj++) { final double[] yieldBumpedPP = y.clone(); yieldBumpedPP[i] += ((pmi == 0) ? SHIFT : -SHIFT); yieldBumpedPP[j] += ((pmj == 0) ? SHIFT : -SHIFT); final YieldAndDiscountCurve curveBumped = new YieldCurve(name, new InterpolatedDoublesCurve(x, yieldBumpedPP, interpolatedCurve.getInterpolator(), true)); MulticurveProviderDiscount providerBumped = new MulticurveProviderDiscount(); for (Currency loopccy : singleCurve.getCurrencies()) { providerBumped.setCurve(loopccy, curveBumped); } for (IborIndex loopibor : singleCurve.getIndexesIbor()) { providerBumped.setCurve(loopibor, curveBumped); } pv[pmi][pmj] = swap.accept(PVDC, providerBumped).getAmount(ccy); } } gammaExpected[i][j] = (pv[0][0] - pv[1][0] - pv[0][1] + pv[1][1]) / (2 * SHIFT * 2 * SHIFT); } } for (int i = 0; i < nbNode; i++) { for (int j = 0; j < nbNode; j++) { if (Math.abs(gammaExpected[i][j]) > 1 || Math.abs(gammaComputed[i][j]) > 1) { // Check only the meaningful numbers assertTrue("CrossGammaSingleCurveCalculator - " + i + " - " + j + " / " + gammaExpected[i][j] + " - " + gammaComputed[i][j], (Math.abs(gammaExpected[i][j] / gammaComputed[i][j] - 1) < TOLERANCE_PV_GAMMA_RELATIF) || // Relative diff small (Math.abs(gammaExpected[i][j] - gammaComputed[i][j]) < TOLERANCE_PV_GAMMA)); // Absolute diff small } } } } @Test public void parSpreadFixedIborBeforeFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate); final double parSpread = swap.accept(PSMQDC, MULTICURVES); final SwapFixedIborDefinition swap0Definition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, RATE_FIXED + parSpread, true); final SwapFixedCoupon<Coupon> swap0 = swap0Definition.toDerivative(referenceDate); final MultipleCurrencyAmount pv = swap0.accept(PVDC, MULTICURVES); assertEquals("ParSpreadCalculator: fixed-coupon swap", pv.getAmount(swap.getFirstLeg().getCurrency()), 0, TOLERANCE_PV); } @Test public void parRateFixedIborBeforeFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate); final double parRate = swap.accept(PRDC, MULTICURVES); final SwapFixedIborDefinition swapATMDefinition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, parRate, true); final SwapFixedCoupon<Coupon> swapATM = swapATMDefinition.toDerivative(referenceDate); final MultipleCurrencyAmount pv = swapATM.accept(PVDC, MULTICURVES); assertEquals("ParSpreadCalculator: fixed-coupon swap", pv.getAmount(swap.getFirstLeg().getCurrency()), 0, TOLERANCE_PV); } @Test public void parRateFixedIborRate0BeforeFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); SwapFixedIborDefinition swap0Definition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, 0.0d, true); final SwapFixedCoupon<Coupon> swap = swap0Definition.toDerivative(referenceDate); final double parRate = swap.accept(PRDC, MULTICURVES); final SwapFixedIborDefinition swapATMDefinition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, parRate, true); final SwapFixedCoupon<Coupon> swapATM = swapATMDefinition.toDerivative(referenceDate); final MultipleCurrencyAmount pv = swapATM.accept(PVDC, MULTICURVES); assertEquals("ParRateCalculator: fixed-coupon swap", pv.getAmount(swap.getFirstLeg().getCurrency()), 0, TOLERANCE_PV); } @Test public void parRateFixedIborRate0AfterFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 22); SwapFixedIborDefinition swap0Definition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, 0.0d, true); final SwapFixedCoupon<Coupon> swap = swap0Definition.toDerivative(referenceDate, FIXING_TS_3_6); final double parRate = swap.accept(PRDC, MULTICURVES); final SwapFixedIborDefinition swapATMDefinition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, parRate, true); final SwapFixedCoupon<Coupon> swapATM = swapATMDefinition.toDerivative(referenceDate, FIXING_TS_3_6); final MultipleCurrencyAmount pv = swapATM.accept(PVDC, MULTICURVES); assertEquals("ParRateCalculator: fixed-coupon swap", pv.getAmount(swap.getFirstLeg().getCurrency()), 0, TOLERANCE_PV); } @Test public void parSpreadFixedIborAfterFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 16); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6); final double parSpread = swap.accept(PSMQDC, MULTICURVES); final SwapFixedIborDefinition swap0Definition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, RATE_FIXED + parSpread, true); final SwapFixedCoupon<Coupon> swap0 = swap0Definition.toDerivative(referenceDate, FIXING_TS_3_6); final MultipleCurrencyAmount pv = swap0.accept(PVDC, MULTICURVES); assertEquals("ParSpreadCalculator: fixed-coupon swap", pv.getAmount(swap.getFirstLeg().getCurrency()), 0, TOLERANCE_PV); } @Test public void parSpreadIborSpreadIborSpreadBeforeFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final Swap<Coupon, Coupon> swap = SWAP_IBORSPREAD_IBORSPREAD_DEFINITION.toDerivative(referenceDate); final double parSpread = swap.accept(PSMQDC, MULTICURVES); final SwapIborIborDefinition swap0Definition = new SwapIborIborDefinition( AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR3M, SPREAD3 + parSpread, true, NYC), AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR6M, SPREAD6, false, NYC)); final Swap<Coupon, Coupon> swap0 = swap0Definition.toDerivative(referenceDate); final MultipleCurrencyAmount pv = swap0.accept(PVDC, MULTICURVES); assertEquals("ParSpreadCalculator: fixed-coupon swap", pv.getAmount(swap.getFirstLeg().getCurrency()), 0, TOLERANCE_PV); } @Test public void parSpreadIborSpreadIborSpreadAfterFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 16); final Swap<Coupon, Coupon> swap = SWAP_IBORSPREAD_IBORSPREAD_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6); final double parSpread = swap.accept(PSMQDC, MULTICURVES); final SwapIborIborDefinition swap0Definition = new SwapIborIborDefinition( AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR3M, SPREAD3 + parSpread, true, NYC), AnnuityCouponIborSpreadDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, NOTIONAL, USDLIBOR6M, SPREAD6, false, NYC)); final Swap<Coupon, Coupon> swap0 = swap0Definition.toDerivative(referenceDate, FIXING_TS_3_6); final MultipleCurrencyAmount pv = swap0.accept(PVDC, MULTICURVES); assertEquals("ParSpreadCalculator: fixed-coupon swap", pv.getAmount(swap.getFirstLeg().getCurrency()), 0, TOLERANCE_PV); } @Test public void pv01CurveParametersBeforeFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate); final ReferenceAmount<Pair<String, Currency>> pv01Computed = swap.accept(PV01CPC, MULTICURVES); final ReferenceAmount<Pair<String, Currency>> pv01Expected = new ReferenceAmount<>(); final MultipleCurrencyParameterSensitivity pvps = PSPVC.calculateSensitivity(swap, MULTICURVES, MULTICURVES.getAllNames()); for (final Pair<String, Currency> nameCcy : pvps.getAllNamesCurrency()) { double total = 0.0; final double[] array = pvps.getSensitivity(nameCcy).getData(); for (final double element : array) { total += element; } total *= BP1; pv01Expected.add(nameCcy, total); } assertEquals("PV01CurveParametersCalculator: fixed-coupon swap", pv01Expected, pv01Computed); } @Test public void todayPaymentFixedIborBeforeFirstFixing() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate); final MultipleCurrencyAmount cash = swap.accept(TPC); assertEquals("TodayPaymentCalculator: fixed-coupon swap", 0.0, cash.getAmount(USDLIBOR3M.getCurrency()), TOLERANCE_PV); assertEquals("TodayPaymentCalculator: fixed-coupon swap", 1, cash.getCurrencyAmounts().length); } @Test public void todayPaymentFixedIborOnFirstIborPayment() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 8, 17); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6); final MultipleCurrencyAmount cash = swap.accept(TPC); assertEquals("TodayPaymentCalculator: fixed-coupon swap", 0.0100 * NOTIONAL * SWAP_FIXED_IBOR_DEFINITION.getIborLeg().getNthPayment(0).getPaymentYearFraction(), cash.getAmount(USDLIBOR3M.getCurrency()), TOLERANCE_PV); assertEquals("TodayPaymentCalculator: fixed-coupon swap", 1, cash.getCurrencyAmounts().length); } @Test public void todayPaymentFixedIborOnFirstFixedPayment() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 11, 19); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6); final MultipleCurrencyAmount cash = swap.accept(TPC); assertEquals("TodayPaymentCalculator: fixed-coupon swap", SWAP_FIXED_IBOR_DEFINITION.getFixedLeg().getNthPayment(0).getAmount() + 0.0140 * NOTIONAL * SWAP_FIXED_IBOR_DEFINITION.getIborLeg().getNthPayment(1).getPaymentYearFraction(), cash.getAmount(USDLIBOR3M.getCurrency()), TOLERANCE_PV); assertEquals("TodayPaymentCalculator: fixed-coupon swap", 1, cash.getCurrencyAmounts().length); } @Test public void todayPaymentFixedIborBetweenPayments() { final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 11, 14); final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6); final MultipleCurrencyAmount cash = swap.accept(TPC); assertEquals("TodayPaymentCalculator: fixed-coupon swap", 0.0, cash.getAmount(USDLIBOR3M.getCurrency()), TOLERANCE_PV); assertEquals("TodayPaymentCalculator: fixed-coupon swap", 1, cash.getCurrencyAmounts().length); } @Test(enabled = false) public void presentValuePerformance() { long startTime, endTime; final int nbTest = 100; final PresentValueDiscountingCalculator pvdCalculator = PresentValueDiscountingCalculator.getInstance(); final PresentValueCurveSensitivityDiscountingCalculator pvcsdCalculator = PresentValueCurveSensitivityDiscountingCalculator.getInstance(); final ParameterSensitivityParameterCalculator<ParameterProviderInterface> psCalculator = new ParameterSensitivityParameterCalculator<>(pvcsdCalculator); final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); final int nbSwap = 100; final MultipleCurrencyAmount[] pv = new MultipleCurrencyAmount[nbSwap]; final MultipleCurrencyMulticurveSensitivity[] pvcs = new MultipleCurrencyMulticurveSensitivity[nbSwap]; final MultipleCurrencyParameterSensitivity[] ps = new MultipleCurrencyParameterSensitivity[nbSwap]; final InstrumentDerivative[] swap = new InstrumentDerivative[nbSwap]; for (int loops = 0; loops < nbSwap; loops++) { final double rate = RATE_FIXED - 0.0050 + loops * BP1; final SwapFixedIborDefinition swapDefinition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, rate, true); swap[loops] = swapDefinition.toDerivative(referenceDate); } startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { for (int loops = 0; loops < nbSwap; loops++) { pv[loops] = swap[loops].accept(pvdCalculator, MULTICURVES); } } endTime = System.currentTimeMillis(); System.out.println("SwapCalculatorTest: " + nbTest + " x " + nbSwap + " swaps (5Y/Q) - present value " + (endTime - startTime) + " ms"); // Performance note: Discounting price: 11-Jun-2014: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 225 ms for 100x100 swaps. startTime = System.currentTimeMillis(); // Swap construction + PV for (int looptest = 0; looptest < nbTest; looptest++) { for (int loops = 0; loops < nbSwap; loops++) { final double rate = RATE_FIXED - 0.0050 + loops * BP1; final SwapFixedIborDefinition swapDefinition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, rate, true); swap[loops] = swapDefinition.toDerivative(referenceDate); } for (int loops = 0; loops < nbSwap; loops++) { pv[loops] = swap[loops].accept(pvdCalculator, MULTICURVES); } } endTime = System.currentTimeMillis(); System.out.println("SwapCalculatorTest: " + nbTest + " x " + nbSwap + " swaps (5Y/Q) - construction + present value " + (endTime - startTime) + " ms"); // Performance note: Discounting price: 11-Jun-2014: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 1280 ms for 100x100 swaps. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { for (int loops = 0; loops < nbSwap; loops++) { pv[loops] = swap[loops].accept(pvdCalculator, MULTICURVES); pvcs[loops] = swap[loops].accept(pvcsdCalculator, MULTICURVES); } } endTime = System.currentTimeMillis(); System.out.println("SwapCalculatorTest: " + nbTest + " x " + nbSwap + " swaps (5Y/Q) - present value + present value curve sensitivity " + (endTime - startTime) + " ms"); // Performance note: Discounting price: 11-Jun-2014: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 930 ms for 100x100 swaps. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { for (int loops = 0; loops < nbSwap; loops++) { pv[loops] = swap[loops].accept(pvdCalculator, MULTICURVES); ps[loops] = psCalculator.calculateSensitivity(swap[loops], MULTICURVES, MULTICURVES.getAllNames()); } } endTime = System.currentTimeMillis(); System.out.println("SwapCalculatorTest: " + nbTest + " x " + nbSwap + " swaps (5Y/Q) - present value + present value parameters sensitivity " + (endTime - startTime) + " ms"); // Performance note: Discounting price: 11-Jun-2014: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 1325 ms for 100x100 swaps. } /** Create a very simplified example of swap and curve to produce a detailed workout of AD for curve sensitivity. */ private static final Interpolator1D LINEAR_FLAT = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); private static final double[] CURVE_TIME = new double[] {0.25, 0.50, 1.00, 2.00 }; private static final double[] CURVE_RATE = new double[] {0.01, 0.0125, 0.0150, 0.0175 }; private static final String CURVE_NAME = "USD All"; private static final YieldAndDiscountCurve CURVE_SIMPLE = new YieldCurve(CURVE_NAME, new InterpolatedDoublesCurve(CURVE_TIME, CURVE_RATE, LINEAR_FLAT, true, CURVE_NAME)); private static final MulticurveProviderDiscount MULTICURVE_SIMPLIFIED = new MulticurveProviderDiscount(); static { MULTICURVE_SIMPLIFIED.setCurve(Currency.USD, CURVE_SIMPLE); MULTICURVE_SIMPLIFIED.setCurve(USDLIBOR3M, CURVE_SIMPLE); } private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2014, 6, 13); private static final ZonedDateTime SPOT_DATE = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, USDLIBOR3M.getSpotLag(), NYC); private static final Period SWAP_SIMPLE_START = Period.ofMonths(6); private static final Period SWAP_SIMPLE_TENOR = Period.ofYears(1); private static final ZonedDateTime SETTLEMENT_DATE_SIMPLE = ScheduleCalculator.getAdjustedDate(SPOT_DATE, SWAP_SIMPLE_START, USDLIBOR3M, NYC); private static final double NOTIONAL_SIMPLE = 100000000; //100m private static final double RATE_FIXED_SIMPLE = 0.0200; private static final SwapFixedIborDefinition SWAP_SIMPLE_DEFINITION = SwapFixedIborDefinition.from(SETTLEMENT_DATE_SIMPLE, SWAP_SIMPLE_TENOR, USD6MLIBOR3M, NOTIONAL_SIMPLE, RATE_FIXED_SIMPLE, true); private static final SwapFixedCoupon<Coupon> SWAP_SIMPLE = SWAP_SIMPLE_DEFINITION.toDerivative(REFERENCE_DATE); @SuppressWarnings("unused") @Test(enabled = false) public void workoutADExample() { MultipleCurrencyMulticurveSensitivity pvcs = SWAP_SIMPLE.accept(PVCSDC, MULTICURVE_SIMPLIFIED); MultipleCurrencyParameterSensitivity ps = PSPVC.calculateSensitivity(SWAP_SIMPLE, MULTICURVE_SIMPLIFIED); } // @Test // public void thetaFixedIborBeforeFirstFixing() { // final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 11); // final MultipleCurrencyAmount theta = THETAC.getTheta(SWAP_FIXED_IBOR_DEFINITION, referenceDate, CURVE_NAMES, CURVES, FIXING_TS_3_6, 1); // final SwapFixedCoupon<Coupon> swapToday = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6, CURVE_NAMES); // final SwapFixedCoupon<Coupon> swapTomorrow = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate.plusDays(1), FIXING_TS_3_6, CURVE_NAMES); // final double pvToday = PVC.visit(swapToday, CURVES); // final YieldCurveBundle tomorrowData = CURVE_ROLLDOWN.rollDown(CURVES, TimeCalculator.getTimeBetween(referenceDate, referenceDate.plusDays(1))); // final double pvTomorrow = PVC.visit(swapTomorrow, tomorrowData); // assertEquals("ThetaCalculator: fixed-coupon swap", pvTomorrow - pvToday, theta.getAmount(USDLIBOR3M.getCurrency()), TOLERANCE_PV); // assertEquals("ThetaCalculator: fixed-coupon swap", 1, theta.getCurrencyAmounts().length); // } // // @Test // public void thetaFixedIborOneDayBeforeFirstFixing() { // final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14); // final MultipleCurrencyAmount theta = THETAC.getTheta(SWAP_FIXED_IBOR_DEFINITION, referenceDate, CURVE_NAMES, CURVES, FIXING_TS_3_6, 1); // final SwapFixedCoupon<Coupon> swapToday = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6, CURVE_NAMES); // final ArrayZonedDateTimeDoubleTimeSeries fixing3extended = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC(new ZonedDateTime[] {DateUtils.getUTCDate(2012, 5, 14), DateUtils.getUTCDate(2012, 5, 15) }, // new double[] {0.0090, 0.0090 }); // final ArrayZonedDateTimeDoubleTimeSeries[] fixing36 = new ArrayZonedDateTimeDoubleTimeSeries[] {fixing3extended, FIXING_TS_6 }; // final SwapFixedCoupon<Coupon> swapTomorrow = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate.plusDays(1), fixing36, CURVE_NAMES); // final double pvToday = PVC.visit(swapToday, CURVES); // final YieldCurveBundle tomorrowData = CURVE_ROLLDOWN.rollDown(CURVES, TimeCalculator.getTimeBetween(referenceDate, referenceDate.plusDays(1))); // final double pvTomorrow = PVC.visit(swapTomorrow, tomorrowData); // assertEquals("ThetaCalculator: fixed-coupon swap", pvTomorrow - pvToday, theta.getAmount(USDLIBOR3M.getCurrency()), TOLERANCE_PV); // assertEquals("ThetaCalculator: fixed-coupon swap", 1, theta.getCurrencyAmounts().length); // } // // @Test // public void thetaFixedIborOverFirstPayment() { // final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 8, 17); // final MultipleCurrencyAmount theta = THETAC.getTheta(SWAP_FIXED_IBOR_DEFINITION, referenceDate, CURVE_NAMES, CURVES, FIXING_TS_3_6, 1); // final SwapFixedCoupon<Coupon> swapToday = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate, FIXING_TS_3_6, CURVE_NAMES); // final SwapFixedCoupon<Coupon> swapTomorrow = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate.plusDays(1), FIXING_TS_3_6, CURVE_NAMES); // final double pvToday = PVC.visit(swapToday, CURVES); // final YieldCurveBundle tomorrowData = CURVE_ROLLDOWN.rollDown(CURVES, TimeCalculator.getTimeBetween(referenceDate, referenceDate.plusDays(1))); // final double pvTomorrow = PVC.visit(swapTomorrow, tomorrowData); // final double todayCash = ((CouponFixed) swapToday.getSecondLeg().getNthPayment(0)).getAmount(); // assertEquals("ThetaCalculator: fixed-coupon swap", pvTomorrow - (pvToday - todayCash), theta.getAmount(USDLIBOR3M.getCurrency()), TOLERANCE_PV); // assertEquals("ThetaCalculator: fixed-coupon swap", 1, theta.getCurrencyAmounts().length); // } }