/** * Copyright (C) 2014 - 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 org.testng.annotations.Test; import org.threeten.bp.LocalDate; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.forex.method.FXMatrix; import com.opengamma.analytics.financial.instrument.NotionalProvider; import com.opengamma.analytics.financial.instrument.annuity.AdjustedDateParameters; import com.opengamma.analytics.financial.instrument.annuity.AnnuityDefinition; import com.opengamma.analytics.financial.instrument.annuity.FloatingAnnuityDefinitionBuilder; import com.opengamma.analytics.financial.instrument.annuity.OffsetAdjustedDateParameters; import com.opengamma.analytics.financial.instrument.annuity.OffsetType; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.payment.CouponDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponFixedFxResetDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborFxResetDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborSpreadDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapDefinition; import com.opengamma.analytics.financial.interestrate.datasets.StandardDataSetsMulticurveEUR; import com.opengamma.analytics.financial.interestrate.datasets.StandardDataSetsMulticurveUSD; import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment; import com.opengamma.analytics.financial.interestrate.swap.derivative.Swap; import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingCalculator; import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.financial.convention.businessday.BusinessDayConventions; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.financial.convention.calendar.MondayToFridayCalendar; import com.opengamma.financial.convention.rolldate.RollConvention; 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; @Test(groups = TestGroup.UNIT) public class SwapCrossCurrencyUsdEurE2ETest { private static final ZonedDateTime VALUATION_DATE = DateUtils.getUTCDate(2014, 1, 22); private static final Currency EUR = Currency.EUR; private static final Currency USD = Currency.USD; private static final Calendar LON = new MondayToFridayCalendar("LON"); private static final Calendar NYC = new MondayToFridayCalendar("NYC"); private static final Calendar TARGET = new MondayToFridayCalendar("TARGET"); private static final AdjustedDateParameters ADJUSTED_DATE_LIBOR_LONNYC = new AdjustedDateParameters(NYC, BusinessDayConventions.MODIFIED_FOLLOWING); // Calendar should be LON+NYC private static final OffsetAdjustedDateParameters OFFSET_ADJ_LIBOR_TAR_2 = new OffsetAdjustedDateParameters(-2, OffsetType.BUSINESS, TARGET, BusinessDayConventions.FOLLOWING); private static final OffsetAdjustedDateParameters OFFSET_ADJ_LIBOR_LON_2 = new OffsetAdjustedDateParameters(-2, OffsetType.BUSINESS, LON, BusinessDayConventions.FOLLOWING); /** Curve providers */ private static final FXMatrix FX_MATRIX = new FXMatrix(EUR, USD, 1.20); private static final Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> MULTICURVE_USD_PAIR = StandardDataSetsMulticurveUSD.getCurvesUSDOisL3(); private static final IborIndex USDLIBOR3M = MULTICURVE_USD_PAIR.getFirst().getIndexesIbor().iterator().next(); private static final Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> MULTICURVE_EUR_PAIR = StandardDataSetsMulticurveEUR.getCurvesEurOisE3(); private static final IborIndex EUREURIBOR3M = MULTICURVE_EUR_PAIR.getFirst().getIndexesIbor().iterator().next(); private static final MulticurveProviderDiscount MULTICURVE = MULTICURVE_USD_PAIR.getFirst(); static { MULTICURVE.setCurve(EUR, MULTICURVE_EUR_PAIR.getFirst().getCurve(EUR)); MULTICURVE.setCurve(EUREURIBOR3M, MULTICURVE_EUR_PAIR.getFirst().getCurve(EUREURIBOR3M)); MULTICURVE.setForexMatrix(FX_MATRIX); } private static final CurveBuildingBlockBundle BLOCK = MULTICURVE_USD_PAIR.getSecond(); static{ BLOCK.addAll(MULTICURVE_EUR_PAIR.getSecond()); } /** EUREURIBOR3M + spread v USDLIBOR3M */ private static final LocalDate EFFECTIVE_DATE = LocalDate.of(2014, 1, 24); private static final LocalDate MATURITY_DATE = LocalDate.of(2016, 1, 24); private static final double SPREAD = 0.0020; private static final boolean PAYER = true; private static final double NOTIONAL_EUR = 100_000_000; // EUR private static final double NOTIONAL_USD = 120_000_000; // USD /** Fixed notional */ private static final SwapDefinition XCCY_EUR_USD_NOT_DEFINITION = xccyEurE3SUsdL3(EFFECTIVE_DATE, MATURITY_DATE, SPREAD, PAYER, NOTIONAL_EUR, NOTIONAL_USD, true); private static final Swap<? extends Payment, ? extends Payment> XCCY_GBP_USD_NOT = XCCY_EUR_USD_NOT_DEFINITION.toDerivative(VALUATION_DATE); /** FX Reset notional */ private static final SwapDefinition XCCY_EUR_USD_FXRESETNOT_DEFINITION = xccyEurE3SUsdL3FXReset(EFFECTIVE_DATE, MATURITY_DATE, SPREAD, PAYER, NOTIONAL_EUR, NOTIONAL_USD); private static final Swap<? extends Payment, ? extends Payment> XCCY_EUR_USD_FXRESETNOT = XCCY_EUR_USD_FXRESETNOT_DEFINITION.toDerivative(VALUATION_DATE); /** Calculators. */ private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance(); /** Tolerances */ private static final double TOLERANCE_PV = 1.0E-2; @Test public void presentValueXNot() { MultipleCurrencyAmount pv = XCCY_GBP_USD_NOT.accept(PVDC, MULTICURVE); double pvUsdExpected = 431944.6868; double pvEurExpected = -731021.1778; assertEquals("XCcy Swap - Present Value - USD", pvUsdExpected, pv.getAmount(USD), TOLERANCE_PV); assertEquals("XCcy Swap - Present Value - EUR", pvEurExpected, pv.getAmount(EUR), TOLERANCE_PV); } @Test public void presentValueFxReset() { MultipleCurrencyAmount pv = XCCY_EUR_USD_FXRESETNOT.accept(PVDC, MULTICURVE); double pvUsdExpected = 518623.5163; double pvEurExpected = -731021.1778; assertEquals("XCcy Swap - Present Value - USD", pvUsdExpected, pv.getAmount(USD), TOLERANCE_PV); assertEquals("XCcy Swap - Present Value - EUR", pvEurExpected, pv.getAmount(EUR), TOLERANCE_PV); } private static SwapDefinition xccyEurE3SUsdL3(final LocalDate effectiveDate, final LocalDate maturityDate, final double spreadEur, boolean payerEur, final double notionalEur, final double notionalUsd, boolean exchangeNotional) { NotionalProvider notionalUsdProvider = new NotionalProvider() { @Override public double getAmount(final LocalDate date) { return notionalUsd; } }; FloatingAnnuityDefinitionBuilder iborUsdBuilder = new FloatingAnnuityDefinitionBuilder().payer(!payerEur). notional(notionalUsdProvider).startDate(effectiveDate).endDate(maturityDate).index(USDLIBOR3M). accrualPeriodFrequency(USDLIBOR3M.getTenor()).rollDateAdjuster(RollConvention.NONE.getRollDateAdjuster(0)). resetDateAdjustmentParameters(ADJUSTED_DATE_LIBOR_LONNYC).accrualPeriodParameters(ADJUSTED_DATE_LIBOR_LONNYC). dayCount(USDLIBOR3M.getDayCount()).fixingDateAdjustmentParameters(OFFSET_ADJ_LIBOR_LON_2). currency(USDLIBOR3M.getCurrency()); if (exchangeNotional) { iborUsdBuilder = iborUsdBuilder. exchangeInitialNotional(true).startDateAdjustmentParameters(ADJUSTED_DATE_LIBOR_LONNYC). exchangeFinalNotional(true).endDateAdjustmentParameters(ADJUSTED_DATE_LIBOR_LONNYC); } AnnuityDefinition<?> legIborEurDefinition = legEurE3Notional(effectiveDate, maturityDate, spreadEur, payerEur, notionalEur, exchangeNotional); AnnuityDefinition<?> legIborUsdDefinition = iborUsdBuilder.build(); return new SwapDefinition(legIborEurDefinition, legIborUsdDefinition); } private static SwapDefinition xccyEurE3SUsdL3FXReset(final LocalDate effectiveDate, final LocalDate maturityDate, final double spreadEur, boolean payerEur, final double notionalEur, final double notionalUsd) { AnnuityDefinition<?> legIborEurDefinition = legEurE3Notional(effectiveDate, maturityDate, spreadEur, payerEur, notionalEur, true); AnnuityDefinition<? extends CouponDefinition> legUsdL3FXResetDefinition; double sign = payerEur ? 1.0d : -1.0d; int nbCpn1 = legIborEurDefinition.getNumberOfPayments() - 2; // Remove notional CouponDefinition[] cpnFxReset = new CouponDefinition[3 * nbCpn1]; for (int loopcpn = 0; loopcpn < nbCpn1; loopcpn++) { CouponIborSpreadDefinition cpnLoop = (CouponIborSpreadDefinition) legIborEurDefinition.getNthPayment(loopcpn + 1); cpnFxReset[3 * loopcpn] = new CouponFixedFxResetDefinition(USD, cpnLoop.getAccrualStartDate(), cpnLoop.getAccrualStartDate(), cpnLoop.getAccrualStartDate(), 1.0d, -sign * notionalUsd, 1.0d, EUR, cpnLoop.getFixingDate(), cpnLoop.getAccrualStartDate()); // Notional cpnFxReset[1 + 3 * loopcpn] = new CouponIborFxResetDefinition(USD, cpnLoop.getPaymentDate(), cpnLoop.getAccrualStartDate(), cpnLoop.getAccrualEndDate(), cpnLoop.getPaymentYearFraction(), sign * notionalUsd, cpnLoop.getFixingDate(), USDLIBOR3M, 0.0, NYC, EUR, cpnLoop.getFixingDate(), cpnLoop.getAccrualStartDate()); cpnFxReset[2 + 3 * loopcpn] = new CouponFixedFxResetDefinition(USD, cpnLoop.getAccrualEndDate(), cpnLoop.getAccrualEndDate(), cpnLoop.getAccrualEndDate(), 1.0d, sign * notionalUsd, 1.0d, EUR, cpnLoop.getFixingDate(), cpnLoop.getAccrualStartDate()); // Notional } legUsdL3FXResetDefinition = new AnnuityDefinition<>(cpnFxReset, NYC); return new SwapDefinition(legIborEurDefinition, legUsdL3FXResetDefinition); } private static AnnuityDefinition<?> legEurE3Notional(final LocalDate effectiveDate, final LocalDate maturityDate, final double spreadEur, boolean payerEur, final double notionalEur, boolean exchangeNotional){ NotionalProvider notionalEurProvider = new NotionalProvider() { @Override public double getAmount(final LocalDate date) { return notionalEur; } }; FloatingAnnuityDefinitionBuilder iborEurBuilder = new FloatingAnnuityDefinitionBuilder().payer(payerEur). notional(notionalEurProvider).startDate(effectiveDate).endDate(maturityDate).index(EUREURIBOR3M). accrualPeriodFrequency(EUREURIBOR3M.getTenor()).rollDateAdjuster(RollConvention.NONE.getRollDateAdjuster(0)). resetDateAdjustmentParameters(ADJUSTED_DATE_LIBOR_LONNYC).accrualPeriodParameters(ADJUSTED_DATE_LIBOR_LONNYC). dayCount(EUREURIBOR3M.getDayCount()).fixingDateAdjustmentParameters(OFFSET_ADJ_LIBOR_TAR_2). currency(EUREURIBOR3M.getCurrency()).spread(spreadEur); if (exchangeNotional) { iborEurBuilder = iborEurBuilder. exchangeInitialNotional(true).startDateAdjustmentParameters(ADJUSTED_DATE_LIBOR_LONNYC). exchangeFinalNotional(true).endDateAdjustmentParameters(ADJUSTED_DATE_LIBOR_LONNYC); } return iborEurBuilder.build(); } }