/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.provider.calculator.generic; import static org.testng.AssertJUnit.assertEquals; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import org.testng.annotations.Test; import org.threeten.bp.Period; import com.opengamma.analytics.financial.forex.method.FXMatrix; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexON; import com.opengamma.analytics.financial.interestrate.annuity.derivative.Annuity; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed; 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.PresentValueCurveSensitivityDiscountingCalculator; import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingCalculator; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity; import com.opengamma.analytics.math.curve.ConstantDoublesCurve; 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.financial.convention.businessday.BusinessDayConventions; import com.opengamma.financial.convention.daycount.DayCounts; import com.opengamma.util.money.Currency; import com.opengamma.util.test.TestGroup; import com.opengamma.util.tuple.DoublesPair; /** * Test. */ @Test(groups = TestGroup.UNIT) public class ZSpreadCalculatorTest { private static final String CURVE_NAME = "Discounting"; private static final PresentValueDiscountingCalculator PV_CALCULATOR = PresentValueDiscountingCalculator.getInstance(); private static final PresentValueCurveSensitivityDiscountingCalculator PVS_CALCULATOR = PresentValueCurveSensitivityDiscountingCalculator.getInstance(); private static final double[] T; private static final double[] R1; private static final double[] R2; private static final double[] R3; private static final double[] R4; private static final MulticurveProviderDiscount CONSTANT_CURVES; private static final MulticurveProviderDiscount MULTI_CURVES; private static final YieldCurve EUR_DISCOUNTING; private static final Annuity<CouponFixed> PAYMENTS; private static final double YIELD = 0.04; private static final Currency CUR = Currency.EUR; private static final ZSpreadCalculator<ParameterProviderInterface> CALCULATOR = new ZSpreadCalculator<>( PV_CALCULATOR, PVS_CALCULATOR); private static final Interpolator1D INTERPOLATOR = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.DOUBLE_QUADRATIC, Interpolator1DFactory.LINEAR_EXTRAPOLATOR, Interpolator1DFactory.LINEAR_EXTRAPOLATOR); static { int n = 5; final CouponFixed[] rateAtYield = new CouponFixed[n]; EUR_DISCOUNTING = YieldCurve.from(ConstantDoublesCurve.from(YIELD, CURVE_NAME)); final Map<Currency, YieldAndDiscountCurve> constantDiscounting = Collections.<Currency, YieldAndDiscountCurve>singletonMap(Currency.EUR, EUR_DISCOUNTING); final Map<IborIndex, YieldAndDiscountCurve> emptyForwardIbor = Collections.emptyMap(); final Map<IndexON, YieldAndDiscountCurve> emptyForwardON = Collections.emptyMap(); final FXMatrix fxMatrix = new FXMatrix(); fxMatrix.addCurrency(Currency.EUR, Currency.USD, 1.2); CONSTANT_CURVES = new MulticurveProviderDiscount(constantDiscounting, emptyForwardIbor, emptyForwardON, fxMatrix); for (int i = 0; i < n; i++) { rateAtYield[i] = new CouponFixed(CUR, 0.5 * (i + 1), 0.5, YIELD); } n = 10; T = new double[n]; R1 = new double[n]; R2 = new double[n]; R3 = new double[n]; R4 = new double[n]; final Random random = new Random(12983); for (int i = 0; i < n; i++) { final double time = 0.5 * (i + 1); T[i] = time + Math.max(0, (0.5 - random.nextDouble()) / 10); R1[i] = 0.02 + time * random.nextGaussian() / 10; R2[i] = 0.03 + time * random.nextGaussian() / 10; R3[i] = 0.04 + time * random.nextGaussian() / 10; R4[i] = 0.04 + time * random.nextGaussian() / 10; } final Map<Currency, YieldAndDiscountCurve> discounting = new HashMap<>(); discounting.put(Currency.EUR, YieldCurve.from(InterpolatedDoublesCurve.from(T, R1, INTERPOLATOR))); discounting.put(Currency.USD, YieldCurve.from(InterpolatedDoublesCurve.from(T, R2, INTERPOLATOR))); final Map<IborIndex, YieldAndDiscountCurve> forwardIbor = new HashMap<>(); forwardIbor.put(new IborIndex(Currency.EUR, Period.ofMonths(6), n, DayCounts.ACT_360, BusinessDayConventions.NONE, false, "Ibor"), YieldCurve.from(InterpolatedDoublesCurve.from(T, R3, INTERPOLATOR))); final Map<IndexON, YieldAndDiscountCurve> forwardON = new HashMap<>(); forwardON.put(new IndexON("ON", Currency.EUR, DayCounts.ACT_360, 0), YieldCurve.from(InterpolatedDoublesCurve.from(T, R4, INTERPOLATOR))); MULTI_CURVES = new MulticurveProviderDiscount(discounting, forwardIbor, forwardON, fxMatrix); PAYMENTS = new Annuity<>(rateAtYield); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullPVCalculator() { new ZSpreadCalculator<>(null, PVS_CALCULATOR); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullPVSCalculator() { new ZSpreadCalculator<>(PV_CALCULATOR, null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullAnnuity1() { CALCULATOR.calculatePriceForZSpread(null, MULTI_CURVES, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullAnnuity2() { CALCULATOR.calculatePriceSensitivityToCurve(null, MULTI_CURVES, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullAnnuity3() { CALCULATOR.calculatePriceSensitivityToZSpread(null, MULTI_CURVES, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullAnnuity4() { CALCULATOR.calculateZSpread(null, MULTI_CURVES, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullAnnuity5() { CALCULATOR.calculateZSpreadSensitivityToCurve(null, MULTI_CURVES, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurves1() { CALCULATOR.calculatePriceForZSpread(PAYMENTS, null, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurves2() { CALCULATOR.calculatePriceSensitivityToCurve(PAYMENTS, null, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurves3() { CALCULATOR.calculatePriceSensitivityToZSpread(PAYMENTS, null, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurves4() { CALCULATOR.calculateZSpread(PAYMENTS, null, 0.04); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurves5() { CALCULATOR.calculateZSpreadSensitivityToCurve(PAYMENTS, null, 0.04); } @Test public void testZeroSpread() { double price = 0; for (int i = 0; i < 5; i++) { price += EUR_DISCOUNTING.getDiscountFactor(0.5 * (i + 1)); } price *= YIELD / 2; assertEquals(0, CALCULATOR.calculateZSpread(PAYMENTS, CONSTANT_CURVES, price), 1e-12); assertEquals(CALCULATOR.calculatePriceForZSpread(PAYMENTS, CONSTANT_CURVES, 0), price, 1e-12); } @Test public void testZSpread() { final double price = PAYMENTS.accept(PV_CALCULATOR, CONSTANT_CURVES).getAmount(Currency.EUR); final double zSpread = CALCULATOR.calculateZSpread(PAYMENTS, MULTI_CURVES, price); final double[] rSpread = new double[R1.length]; for (int i = 0; i < rSpread.length; i++) { rSpread[i] = R1[i] + zSpread; } final MulticurveProviderDiscount multicurves = new MulticurveProviderDiscount(MULTI_CURVES.copy()); multicurves.replaceCurve(Currency.EUR, YieldCurve.from(InterpolatedDoublesCurve.from(T, rSpread, INTERPOLATOR))); assertEquals(price, PAYMENTS.accept(PV_CALCULATOR, multicurves).getAmount(Currency.EUR), 1e-12); assertEquals(price, CALCULATOR.calculatePriceForZSpread(PAYMENTS, MULTI_CURVES, zSpread), 1e-12); } @Test public void testPriceSensitivityToZSpread() { final double price = PAYMENTS.accept(PV_CALCULATOR, CONSTANT_CURVES).getAmount(Currency.EUR); final double zSpread = CALCULATOR.calculateZSpread(PAYMENTS, MULTI_CURVES, price); final double eps = 1e-3; final double[] rSpread = new double[R1.length]; for (int i = 0; i < rSpread.length; i++) { rSpread[i] = R1[i] + zSpread + eps; } final MulticurveProviderDiscount multicurves = new MulticurveProviderDiscount(MULTI_CURVES.copy()); multicurves.replaceCurve(Currency.EUR, YieldCurve.from(InterpolatedDoublesCurve.from(T, rSpread, INTERPOLATOR))); final double newPrice = PAYMENTS.accept(PV_CALCULATOR, multicurves).getAmount(Currency.EUR); assertEquals((newPrice - price) / eps, CALCULATOR.calculatePriceSensitivityToZSpread(PAYMENTS, MULTI_CURVES, zSpread), eps); } @Test public void testSensitivities() { double zSpread = 0.06; final double dPdZ = CALCULATOR.calculatePriceSensitivityToZSpread(PAYMENTS, CONSTANT_CURVES, zSpread); final Map<String, List<DoublesPair>> dZdC = CALCULATOR.calculateZSpreadSensitivityToCurve(PAYMENTS, CONSTANT_CURVES, zSpread); Map<String, List<DoublesPair>> dPdC = CALCULATOR.calculatePriceSensitivityToCurve(PAYMENTS, CONSTANT_CURVES, zSpread); assertEquals(dZdC.size(), dPdC.size()); Iterator<Entry<String, List<DoublesPair>>> iter1 = dZdC.entrySet().iterator(); Iterator<Entry<String, List<DoublesPair>>> iter2 = dPdC.entrySet().iterator(); while (iter1.hasNext()) { final Entry<String, List<DoublesPair>> e1 = iter1.next(); final Entry<String, List<DoublesPair>> e2 = iter2.next(); assertEquals(e1.getKey(), CURVE_NAME); assertEquals(e2.getKey(), CURVE_NAME); final List<DoublesPair> pairs1 = e1.getValue(); final List<DoublesPair> pairs2 = e2.getValue(); assertEquals(pairs1.size(), 5); assertEquals(pairs2.size(), 5); for (int i = 0; i < 5; i++) { assertEquals(pairs1.get(i).first, pairs2.get(i).first, 1e-15); assertEquals(-pairs2.get(i).second / pairs1.get(i).second, dPdZ, 1e-15); } } zSpread = 0.0; dPdC = CALCULATOR.calculatePriceSensitivityToCurve(PAYMENTS, CONSTANT_CURVES, zSpread); final MultipleCurrencyMulticurveSensitivity mcms = PAYMENTS.accept(PVS_CALCULATOR, CONSTANT_CURVES); assertEquals(mcms.getCurrencies().size(), 1); final Map<String, List<DoublesPair>> pvSensitivity = mcms.getSensitivity(CUR).getYieldDiscountingSensitivities(); iter1 = dPdC.entrySet().iterator(); iter2 = pvSensitivity.entrySet().iterator(); while (iter1.hasNext()) { final Entry<String, List<DoublesPair>> e1 = iter1.next(); final Entry<String, List<DoublesPair>> e2 = iter2.next(); assertEquals(e1.getKey(), CURVE_NAME); assertEquals(e2.getKey(), CURVE_NAME); final List<DoublesPair> pairs1 = e1.getValue(); final List<DoublesPair> pairs2 = e2.getValue(); assertEquals(pairs1.size(), 5); assertEquals(pairs2.size(), 5); for (int i = 0; i < 5; i++) { assertEquals(pairs1.get(i).first, pairs2.get(i).first, 1e-15); assertEquals(pairs2.get(i).second, pairs1.get(i).second, 1e-15); } } } @Test public void testZSpreadSensitivityToCurve() { } @Test public void testPriceSensitivityToCurve() { } }