/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.provider.description.interestrate; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import org.testng.annotations.Test; import com.opengamma.analytics.financial.legalentity.LegalEntity; import com.opengamma.analytics.financial.legalentity.LegalEntityFilter; import com.opengamma.analytics.financial.legalentity.LegalEntityShortName; import com.opengamma.analytics.financial.model.interestrate.curve.DiscountCurve; 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.YieldPeriodicCurve; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.ForwardSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.SimplyCompoundedForwardSensitivity; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolator; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.util.money.Currency; import com.opengamma.util.test.TestGroup; import com.opengamma.util.tuple.DoublesPair; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * Test {@link IssuerProviderIssuerDecoratedSpreadPeriodicTest} */ @Test(groups = TestGroup.UNIT) public class IssuerProviderIssuerDecoratedSpreadPeriodicTest { /* Building curves */ private static final CombinedInterpolatorExtrapolator INTERPOLATOR = new CombinedInterpolatorExtrapolator( Interpolator1DFactory.LINEAR_INSTANCE, Interpolator1DFactory.FLAT_EXTRAPOLATOR_INSTANCE, Interpolator1DFactory.FLAT_EXTRAPOLATOR_INSTANCE); private static final YieldAndDiscountCurve YIELD_CURVE_DSC; private static final YieldAndDiscountCurve YIELD_CURVE_YLD; private static final YieldAndDiscountCurve YIELD_CURVE_PRD; private static final IssuerProviderDiscount ISSUER_PROVIDER = new IssuerProviderDiscount(); private static final String CURVE_NAME_DSC = "testIssuerCurve1"; private static final Double[] TIME_DSC = new Double[] {0.1, 0.5, 1.0, 2.0, 5.0, 10.0 }; private static final Double[] FACTOR_DSC = new Double[] {0.99, 0.98, 0.975, 0.96, 0.9, 0.81 }; private static final String ISSUER_NAME_DSC = "testIssuer1"; static { InterpolatedDoublesCurve interpolatedCurve = InterpolatedDoublesCurve.fromSorted(TIME_DSC, FACTOR_DSC, INTERPOLATOR, CURVE_NAME_DSC); YIELD_CURVE_DSC = new DiscountCurve(CURVE_NAME_DSC, interpolatedCurve); LegalEntityFilter<LegalEntity> filter = new LegalEntityShortName(); ISSUER_PROVIDER.setCurve(Pairs.of((Object) ISSUER_NAME_DSC, filter), YIELD_CURVE_DSC); } private static final String CURVE_NAME_YLD = "testIssuerCurve2"; private static final Double[] TIME_YLD = new Double[] {0.1, 0.5, 1.0, 2.0, 5.0, 7.0, 10.0, 15.0 }; private static final Double[] RATE_YLD = new Double[] {0.01, 0.015, 0.015, 0.02, 0.02, 0.025, 0.0125, 0.03 }; private static final String ISSUER_NAME_YLD = "testIssuer2"; static { InterpolatedDoublesCurve interpolatedCurve = InterpolatedDoublesCurve.fromSorted(TIME_YLD, RATE_YLD, INTERPOLATOR, CURVE_NAME_YLD); YIELD_CURVE_YLD = new YieldCurve(CURVE_NAME_YLD, interpolatedCurve); LegalEntityFilter<LegalEntity> filter = new LegalEntityShortName(); ISSUER_PROVIDER.setCurve(Pairs.of((Object) ISSUER_NAME_YLD, filter), YIELD_CURVE_YLD); } private static final String CURVE_NAME_PRD = "testPRDcountCurve"; private static final Double[] TIME_PRD = new Double[] {0.25, 0.5, 1.0, 3.0, 5.0, 10.0 }; private static final Double[] RATE_PRD = new Double[] {0.02, 0.02, 0.025, 0.03, 0.03, 0.035 }; private static final Currency USD = Currency.USD; static { InterpolatedDoublesCurve interpolatedCurve = InterpolatedDoublesCurve.fromSorted(TIME_PRD, RATE_PRD, INTERPOLATOR, CURVE_NAME_PRD); YIELD_CURVE_PRD = YieldPeriodicCurve.from(2, interpolatedCurve); ISSUER_PROVIDER.setCurve(USD, YIELD_CURVE_PRD); } /* Variables used for testing */ private static final List<ForwardSensitivity> FWD_SENSITIVITY_LIST = new ArrayList<>(); static { ForwardSensitivity fwdSense = new SimplyCompoundedForwardSensitivity(0.75, 1.0, 0.25, 124.0); FWD_SENSITIVITY_LIST.add(fwdSense); } private static final List<DoublesPair> POINT_SENSITIVITY_LIST = new ArrayList<>(); static { DoublesPair pair = DoublesPair.of(0.4, 1.0); POINT_SENSITIVITY_LIST.add(pair); } private static final LegalEntity ISSUER_DSC = new LegalEntity(null, ISSUER_NAME_DSC, null, null, null); private static final LegalEntity ISSUER_YLD = new LegalEntity(null, ISSUER_NAME_YLD, null, null, null); private static final double[] KEYS = new double[] {-1.0, 0.0, 1.35, 12.5, 30.0 }; private static final double NUM_KEYS = KEYS.length; private static final double TOL = 1.0e-12; /** * add spread */ @Test public void spreadTest() { double sampleSpread1 = 0.05; double sampleSpread2 = 0.025; IssuerProviderIssuerDecoratedSpreadPeriodic providerWithSpread1 = new IssuerProviderIssuerDecoratedSpreadPeriodic( ISSUER_PROVIDER, ISSUER_DSC, sampleSpread1, 1); IssuerProviderIssuerDecoratedSpreadPeriodic providerWithSpread2 = new IssuerProviderIssuerDecoratedSpreadPeriodic( providerWithSpread1.getIssuerProvider(), ISSUER_YLD, sampleSpread2, 2); for (int i = 0; i < NUM_KEYS; ++i) { double zeroEquiv1 = Math.pow(ISSUER_PROVIDER.getDiscountFactor(ISSUER_DSC, KEYS[i]), -1.0 / KEYS[i]) - 1.0 + sampleSpread1; double disExpected1 = Math.pow(1.0 + zeroEquiv1, -KEYS[i]); double disComputed1 = providerWithSpread2.getDiscountFactor(ISSUER_DSC, KEYS[i]); assertEquals("accesserTest", disExpected1, disComputed1, TOL); double zeroEquiv2 = 2.0 * Math.pow(ISSUER_PROVIDER.getDiscountFactor(ISSUER_YLD, KEYS[i]), -1.0 / 2.0 / KEYS[i]) - 2.0 + sampleSpread2; double disExpected2 = Math.pow(1.0 + zeroEquiv2 / 2.0, -2.0 * KEYS[i]); double disComputed2 = providerWithSpread2.getDiscountFactor(ISSUER_YLD, KEYS[i]); assertEquals("spreadTest", disExpected2, disComputed2, TOL); double disExpected3 = ISSUER_PROVIDER.getMulticurveProvider().getDiscountFactor(USD, KEYS[i]); double disComputed3 = providerWithSpread2.getMulticurveProvider().getDiscountFactor(USD, KEYS[i]); assertEquals("spreadTest", disExpected3, disComputed3, TOL); } assertTrue(providerWithSpread2.getName(ISSUER_DSC).equals(ISSUER_PROVIDER.getName(ISSUER_DSC))); assertTrue(providerWithSpread2.getName(ISSUER_YLD).equals(ISSUER_PROVIDER.getName(ISSUER_YLD))); Set<Pair<Object, LegalEntityFilter<LegalEntity>>> issuers = providerWithSpread2.getIssuers(); assertTrue(issuers.equals(ISSUER_PROVIDER.getIssuers())); assertTrue(providerWithSpread2.getAllNames().equals(ISSUER_PROVIDER.getAllNames())); assertTrue(providerWithSpread2.getAllCurveNames().equals(ISSUER_PROVIDER.getAllCurveNames())); assertEquals("spreadTest", providerWithSpread2.getNumberOfParameters(CURVE_NAME_DSC), ISSUER_PROVIDER.getNumberOfParameters(CURVE_NAME_DSC)); assertEquals("spreadTest", providerWithSpread2.getNumberOfParameters(CURVE_NAME_YLD), ISSUER_PROVIDER.getNumberOfParameters(CURVE_NAME_YLD)); assertEquals("spreadTest", providerWithSpread2.getNumberOfParameters(CURVE_NAME_PRD), ISSUER_PROVIDER.getNumberOfParameters(CURVE_NAME_PRD)); assertEquals("spreadTest", new ArrayList<>(), providerWithSpread2.getUnderlyingCurvesNames(CURVE_NAME_DSC)); assertEquals("spreadTest", new ArrayList<>(), providerWithSpread1.getUnderlyingCurvesNames(CURVE_NAME_YLD)); LegalEntityFilter<LegalEntity> filter = new LegalEntityShortName(); assertEquals("accesserTest", CURVE_NAME_DSC, providerWithSpread2.getName(Pairs.of((Object) ISSUER_NAME_DSC, filter))); assertEquals("accesserTest", CURVE_NAME_YLD, providerWithSpread2.getName(Pairs.of((Object) ISSUER_NAME_YLD, filter))); IssuerProviderIssuerDecoratedSpreadPeriodic providerWithTinySpread = new IssuerProviderIssuerDecoratedSpreadPeriodic( ISSUER_PROVIDER, ISSUER_DSC, TOL, 1); assertArrayRelative("accesserTest", ISSUER_PROVIDER.parameterForwardSensitivity(CURVE_NAME_DSC, FWD_SENSITIVITY_LIST), providerWithTinySpread.parameterForwardSensitivity(CURVE_NAME_DSC, FWD_SENSITIVITY_LIST), TOL); assertArrayRelative("accesserTest", ISSUER_PROVIDER.parameterForwardSensitivity(CURVE_NAME_YLD, FWD_SENSITIVITY_LIST), providerWithTinySpread.parameterForwardSensitivity(CURVE_NAME_YLD, FWD_SENSITIVITY_LIST), TOL); assertArrayRelative("accesserTest", ISSUER_PROVIDER.parameterSensitivity(CURVE_NAME_DSC, POINT_SENSITIVITY_LIST), providerWithTinySpread.parameterSensitivity(CURVE_NAME_DSC, POINT_SENSITIVITY_LIST), TOL * 10.); assertArrayRelative("accesserTest", ISSUER_PROVIDER.parameterSensitivity(CURVE_NAME_YLD, POINT_SENSITIVITY_LIST), providerWithTinySpread.parameterSensitivity(CURVE_NAME_YLD, POINT_SENSITIVITY_LIST), TOL * 10.); } /** * sensitivity test against finite difference approximation */ @Test public void sensitivityFiniteDifferenceTest() { double eps = 1.0e-7; double spreadSemiannual = -0.02; int nPeriodS = 2; double spreadQuarterly = 0.06; int nPeriodQ = 4; IssuerProviderIssuerDecoratedSpreadPeriodic providerWithSpread = new IssuerProviderIssuerDecoratedSpreadPeriodic( ISSUER_PROVIDER, ISSUER_DSC, spreadSemiannual, nPeriodS); IssuerProviderIssuerDecoratedSpreadPeriodic providerWithSpreadDouble = new IssuerProviderIssuerDecoratedSpreadPeriodic( providerWithSpread, ISSUER_YLD, spreadQuarterly, nPeriodQ); // discount curve { double[] senseComputed = providerWithSpreadDouble.parameterSensitivity(CURVE_NAME_DSC, POINT_SENSITIVITY_LIST); int nParams = FACTOR_DSC.length; double[] senseExpected = new double[nParams]; for (int i = 0; i < nParams; ++i) { LegalEntityFilter<LegalEntity> filter = new LegalEntityShortName(); IssuerProviderDiscount issuerDiscountUp = new IssuerProviderDiscount(); IssuerProviderDiscount issuerDiscountDw = new IssuerProviderDiscount(); Double[] pramsUp = Arrays.copyOf(FACTOR_DSC, nParams); Double[] pramsDw = Arrays.copyOf(FACTOR_DSC, nParams); pramsUp[i] += eps; pramsDw[i] -= eps; InterpolatedDoublesCurve interpolatedCurveUp = InterpolatedDoublesCurve.fromSorted(TIME_DSC, pramsUp, INTERPOLATOR, CURVE_NAME_DSC); DiscountCurve discountCurveUp = new DiscountCurve(CURVE_NAME_DSC, interpolatedCurveUp); issuerDiscountUp.setCurve(Pairs.of((Object) ISSUER_NAME_DSC, filter), discountCurveUp); double up = issuerDiscountUp.getIssuerCurve(CURVE_NAME_DSC).getInterestRate( POINT_SENSITIVITY_LIST.get(0).getFirst()); up = addShiftToPrdCmpRates(up, spreadSemiannual, nPeriodS); InterpolatedDoublesCurve interpolatedCurveDw = InterpolatedDoublesCurve.fromSorted(TIME_DSC, pramsDw, INTERPOLATOR, CURVE_NAME_DSC); DiscountCurve discountCurveDw = new DiscountCurve(CURVE_NAME_DSC, interpolatedCurveDw); issuerDiscountDw.setCurve(Pairs.of((Object) ISSUER_NAME_DSC, filter), discountCurveDw); double dw = issuerDiscountDw.getIssuerCurve(CURVE_NAME_DSC).getInterestRate( POINT_SENSITIVITY_LIST.get(0).getFirst()); dw = addShiftToPrdCmpRates(dw, spreadSemiannual, nPeriodS); senseExpected[i] = 0.5 * (up - dw) * POINT_SENSITIVITY_LIST.get(0).getSecond() / eps; } assertArrayRelative("sensitivityFiniteDifferenceDiscountTest", senseExpected, senseComputed, eps); } // yield curve { double[] senseComputed = providerWithSpreadDouble.parameterSensitivity(CURVE_NAME_YLD, POINT_SENSITIVITY_LIST); int nParams = RATE_YLD.length; double[] senseExpected = new double[nParams]; for (int i = 0; i < nParams; ++i) { LegalEntityFilter<LegalEntity> filter = new LegalEntityShortName(); IssuerProviderDiscount issuerDiscountUp = new IssuerProviderDiscount(); IssuerProviderDiscount issuerDiscountDw = new IssuerProviderDiscount(); Double[] pramsUp = Arrays.copyOf(RATE_YLD, nParams); Double[] pramsDw = Arrays.copyOf(RATE_YLD, nParams); pramsUp[i] += eps; pramsDw[i] -= eps; InterpolatedDoublesCurve interpolatedCurveUp = InterpolatedDoublesCurve.fromSorted(TIME_YLD, pramsUp, INTERPOLATOR, CURVE_NAME_YLD); YieldCurve discountCurveUp = new YieldCurve(CURVE_NAME_YLD, interpolatedCurveUp); issuerDiscountUp.setCurve(Pairs.of((Object) ISSUER_NAME_YLD, filter), discountCurveUp); double up = issuerDiscountUp.getIssuerCurve(CURVE_NAME_YLD).getInterestRate( POINT_SENSITIVITY_LIST.get(0).getFirst()); up = addShiftToPrdCmpRates(up, spreadQuarterly, nPeriodQ); InterpolatedDoublesCurve interpolatedCurveDw = InterpolatedDoublesCurve.fromSorted(TIME_YLD, pramsDw, INTERPOLATOR, CURVE_NAME_YLD); YieldCurve discountCurveDw = new YieldCurve(CURVE_NAME_YLD, interpolatedCurveDw); issuerDiscountDw.setCurve(Pairs.of((Object) ISSUER_NAME_YLD, filter), discountCurveDw); double dw = issuerDiscountDw.getIssuerCurve(CURVE_NAME_YLD).getInterestRate( POINT_SENSITIVITY_LIST.get(0).getFirst()); dw = addShiftToPrdCmpRates(dw, spreadQuarterly, nPeriodQ); senseExpected[i] = 0.5 * (up - dw) * POINT_SENSITIVITY_LIST.get(0).getSecond() / eps; } assertArrayRelative("sensitivityFiniteDifferenceDiscountTest", senseExpected, senseComputed, eps); } // currency-based curve is not affected assertArrayRelative("sensitivityFiniteDifferenceDiscountTest", ISSUER_PROVIDER.parameterSensitivity(CURVE_NAME_PRD, POINT_SENSITIVITY_LIST), providerWithSpreadDouble.parameterSensitivity(CURVE_NAME_PRD, POINT_SENSITIVITY_LIST), TOL); } private double addShiftToPrdCmpRates(double interstRate, double spread, int nPeriod) { return nPeriod * Math.log(Math.exp(interstRate / nPeriod) + spread / nPeriod); } /** * copy is not supported, although underlying IssuerProviderDiscount may support copy */ @Test(expectedExceptions = UnsupportedOperationException.class) public void copyFailTest() { IssuerProviderIssuerDecoratedSpreadPeriodic wrapper = new IssuerProviderIssuerDecoratedSpreadPeriodic( ISSUER_PROVIDER, ISSUER_DSC, 0.1, 1); wrapper.copy(); } private static void assertArrayRelative(String message, double[] expected, double[] obtained, double relativeTol) { int nData = expected.length; assertEquals(message, nData, obtained.length); for (int i = 0; i < nData; ++i) { assertRelative(message, expected[i], obtained[i], relativeTol); } } private static void assertRelative(String message, double expected, double obtained, double relativeTol) { double ref = Math.max(Math.abs(expected), 1.0); assertEquals(message, expected, obtained, ref * relativeTol); } }