/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer; import static com.opengamma.strata.basics.currency.Currency.GBP; import static com.opengamma.strata.basics.currency.Currency.USD; import static com.opengamma.strata.basics.date.DayCounts.ACT_365F; import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg; import static com.opengamma.strata.collect.TestHelper.coverBeanEquals; import static com.opengamma.strata.collect.TestHelper.coverImmutableBean; import static com.opengamma.strata.collect.TestHelper.date; import static com.opengamma.strata.pricer.CompoundedRateType.CONTINUOUS; import static com.opengamma.strata.pricer.CompoundedRateType.PERIODIC; import static org.testng.Assert.assertEquals; import java.time.LocalDate; import java.util.Optional; import org.testng.annotations.Test; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.market.ValueType; import com.opengamma.strata.market.curve.CurveMetadata; import com.opengamma.strata.market.curve.CurveName; import com.opengamma.strata.market.curve.Curves; import com.opengamma.strata.market.curve.DefaultCurveMetadata; import com.opengamma.strata.market.curve.InterpolatedNodalCurve; import com.opengamma.strata.market.curve.interpolator.CurveInterpolator; import com.opengamma.strata.market.curve.interpolator.CurveInterpolators; import com.opengamma.strata.market.param.CurrencyParameterSensitivities; /** * Test {@link ZeroRateDiscountFactors}. */ @Test public class ZeroRateDiscountFactorsTest { private static final LocalDate DATE_VAL = date(2015, 6, 4); private static final LocalDate DATE_AFTER = date(2015, 7, 30); private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR; private static final CurveName NAME = CurveName.of("TestCurve"); private static final CurveMetadata METADATA = Curves.zeroRates(NAME, ACT_365F); private static final InterpolatedNodalCurve CURVE = InterpolatedNodalCurve.of(METADATA, DoubleArray.of(0, 10), DoubleArray.of(1, 2), INTERPOLATOR); private static final InterpolatedNodalCurve CURVE2 = InterpolatedNodalCurve.of(METADATA, DoubleArray.of(0, 10), DoubleArray.of(2, 3), INTERPOLATOR); private static final double SPREAD = 0.05; private static final double TOL = 1.0e-12; private static final double TOL_FD = 1.0e-8; private static final double EPS = 1.0e-6; //------------------------------------------------------------------------- public void test_of() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); assertEquals(test.getCurrency(), GBP); assertEquals(test.getValuationDate(), DATE_VAL); assertEquals(test.getCurve(), CURVE); assertEquals(test.getParameterCount(), CURVE.getParameterCount()); assertEquals(test.getParameter(0), CURVE.getParameter(0)); assertEquals(test.getParameterMetadata(0), CURVE.getParameterMetadata(0)); assertEquals(test.withParameter(0, 1d).getCurve(), CURVE.withParameter(0, 1d)); assertEquals(test.withPerturbation((i, v, m) -> v + 1d).getCurve(), CURVE.withPerturbation((i, v, m) -> v + 1d)); assertEquals(test.findData(CURVE.getName()), Optional.of(CURVE)); assertEquals(test.findData(CurveName.of("Rubbish")), Optional.empty()); } public void test_of_badCurve() { InterpolatedNodalCurve notYearFraction = InterpolatedNodalCurve.of( Curves.prices(NAME), DoubleArray.of(0, 10), DoubleArray.of(1, 2), INTERPOLATOR); InterpolatedNodalCurve notZeroRate = InterpolatedNodalCurve.of( Curves.discountFactors(NAME, ACT_365F), DoubleArray.of(0, 10), DoubleArray.of(1, 2), INTERPOLATOR); CurveMetadata noDayCountMetadata = DefaultCurveMetadata.builder() .curveName(NAME) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.ZERO_RATE) .build(); InterpolatedNodalCurve notDayCount = InterpolatedNodalCurve.of( noDayCountMetadata, DoubleArray.of(0, 10), DoubleArray.of(1, 2), INTERPOLATOR); assertThrowsIllegalArg(() -> ZeroRateDiscountFactors.of(GBP, DATE_VAL, notYearFraction)); assertThrowsIllegalArg(() -> ZeroRateDiscountFactors.of(GBP, DATE_VAL, notZeroRate)); assertThrowsIllegalArg(() -> ZeroRateDiscountFactors.of(GBP, DATE_VAL, notDayCount)); } //------------------------------------------------------------------------- public void test_discountFactor() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double expected = Math.exp(-relativeYearFraction * CURVE.yValue(relativeYearFraction)); assertEquals(test.discountFactor(DATE_AFTER), expected); } public void test_discountFactorTimeDerivative() { DiscountFactors test = DiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double expectedP = test.discountFactor(relativeYearFraction + EPS); double expectedM = test.discountFactor(relativeYearFraction - EPS); assertEquals(test.discountFactorTimeDerivative(relativeYearFraction), (expectedP - expectedM) / (2 * EPS), TOL_FD); } //------------------------------------------------------------------------- public void test_zeroRate() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double discountFactor = test.discountFactor(DATE_AFTER); double zeroRate = test.zeroRate(DATE_AFTER); assertEquals(Math.exp(-zeroRate * relativeYearFraction), discountFactor); } //------------------------------------------------------------------------- public void test_discountFactorWithSpread_continuous() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double expected = Math.exp(-relativeYearFraction * (CURVE.yValue(relativeYearFraction) + SPREAD)); assertEquals(test.discountFactorWithSpread(DATE_AFTER, SPREAD, CONTINUOUS, 0), expected, TOL); } public void test_discountFactorWithSpread_periodic() { int periodPerYear = 4; ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double discountFactorBase = test.discountFactor(DATE_AFTER); double rate = (Math.pow(discountFactorBase, -1d / periodPerYear / relativeYearFraction) - 1d) * periodPerYear; double expected = discountFactorFromPeriodicallyCompoundedRate(rate + SPREAD, periodPerYear, relativeYearFraction); assertEquals(test.discountFactorWithSpread(DATE_AFTER, SPREAD, PERIODIC, periodPerYear), expected, TOL); } public void test_discountFactorWithSpread_smallYearFraction() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); assertEquals(test.discountFactorWithSpread(DATE_VAL, SPREAD, PERIODIC, 1), 1d, TOL); } private double discountFactorFromPeriodicallyCompoundedRate(double rate, double periodPerYear, double time) { return Math.pow(1d + rate / periodPerYear, -periodPerYear * time); } //------------------------------------------------------------------------- public void test_zeroRatePointSensitivity() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double df = Math.exp(-relativeYearFraction * CURVE.yValue(relativeYearFraction)); ZeroRateSensitivity expected = ZeroRateSensitivity.of(GBP, relativeYearFraction, -df * relativeYearFraction); assertEquals(test.zeroRatePointSensitivity(DATE_AFTER), expected); } public void test_zeroRatePointSensitivity_sensitivityCurrency() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double df = Math.exp(-relativeYearFraction * CURVE.yValue(relativeYearFraction)); ZeroRateSensitivity expected = ZeroRateSensitivity.of(GBP, relativeYearFraction, USD, -df * relativeYearFraction); assertEquals(test.zeroRatePointSensitivity(DATE_AFTER, USD), expected); } //------------------------------------------------------------------------- public void test_zeroRatePointSensitivityWithSpread_continous() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double df = Math.exp(-relativeYearFraction * (CURVE.yValue(relativeYearFraction) + SPREAD)); ZeroRateSensitivity expected = ZeroRateSensitivity.of(GBP, relativeYearFraction, -df * relativeYearFraction); assertEquals(test.zeroRatePointSensitivityWithSpread(DATE_AFTER, SPREAD, CONTINUOUS, 0), expected); } public void test_zeroRatePointSensitivityWithSpread_sensitivityCurrency_continous() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double df = Math.exp(-relativeYearFraction * (CURVE.yValue(relativeYearFraction) + SPREAD)); ZeroRateSensitivity expected = ZeroRateSensitivity.of(GBP, relativeYearFraction, USD, -df * relativeYearFraction); assertEquals(test.zeroRatePointSensitivityWithSpread(DATE_AFTER, USD, SPREAD, CONTINUOUS, 0), expected); } public void test_zeroRatePointSensitivityWithSpread_periodic() { int periodPerYear = 4; ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double discountFactorUp = Math.exp(-(CURVE.yValue(relativeYearFraction) + EPS) * relativeYearFraction); double discountFactorDw = Math.exp(-(CURVE.yValue(relativeYearFraction) - EPS) * relativeYearFraction); double rateUp = (Math.pow(discountFactorUp, -1d / periodPerYear / relativeYearFraction) - 1d) * periodPerYear; double rateDw = (Math.pow(discountFactorDw, -1d / periodPerYear / relativeYearFraction) - 1d) * periodPerYear; double expectedValue = 0.5 / EPS * ( discountFactorFromPeriodicallyCompoundedRate(rateUp + SPREAD, periodPerYear, relativeYearFraction) - discountFactorFromPeriodicallyCompoundedRate(rateDw + SPREAD, periodPerYear, relativeYearFraction)); ZeroRateSensitivity computed = test.zeroRatePointSensitivityWithSpread( DATE_AFTER, SPREAD, PERIODIC, periodPerYear); assertEquals(computed.getSensitivity(), expectedValue, EPS); assertEquals(computed.getCurrency(), GBP); assertEquals(computed.getCurveCurrency(), GBP); assertEquals(computed.getYearFraction(), relativeYearFraction); } public void test_zeroRatePointSensitivityWithSpread_sensitivityCurrency_periodic() { int periodPerYear = 4; ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double discountFactorUp = Math.exp(-(CURVE.yValue(relativeYearFraction) + EPS) * relativeYearFraction); double discountFactorDw = Math.exp(-(CURVE.yValue(relativeYearFraction) - EPS) * relativeYearFraction); double rateUp = (Math.pow(discountFactorUp, -1d / periodPerYear / relativeYearFraction) - 1d) * periodPerYear; double rateDw = (Math.pow(discountFactorDw, -1d / periodPerYear / relativeYearFraction) - 1d) * periodPerYear; double expectedValue = 0.5 / EPS * ( discountFactorFromPeriodicallyCompoundedRate(rateUp + SPREAD, periodPerYear, relativeYearFraction) - discountFactorFromPeriodicallyCompoundedRate(rateDw + SPREAD, periodPerYear, relativeYearFraction)); ZeroRateSensitivity computed = test .zeroRatePointSensitivityWithSpread(DATE_AFTER, USD, SPREAD, PERIODIC, periodPerYear); assertEquals(computed.getSensitivity(), expectedValue, EPS); assertEquals(computed.getCurrency(), USD); assertEquals(computed.getCurveCurrency(), GBP); assertEquals(computed.getYearFraction(), relativeYearFraction); } public void test_zeroRatePointSensitivityWithSpread_smallYearFraction() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); ZeroRateSensitivity expected = ZeroRateSensitivity.of(GBP, 0d, -0d); assertEquals(test.zeroRatePointSensitivityWithSpread(DATE_VAL, SPREAD, CONTINUOUS, 0), expected); } public void test_zeroRatePointSensitivityWithSpread_sensitivityCurrency_smallYearFraction() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); ZeroRateSensitivity expected = ZeroRateSensitivity.of(GBP, 0d, USD, -0d); assertEquals(test.zeroRatePointSensitivityWithSpread(DATE_VAL, USD, SPREAD, PERIODIC, 2), expected); } //------------------------------------------------------------------------- public void test_unitParameterSensitivity() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); ZeroRateSensitivity sens = test.zeroRatePointSensitivity(DATE_AFTER); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); CurrencyParameterSensitivities expected = CurrencyParameterSensitivities.of( CURVE.yValueParameterSensitivity(relativeYearFraction) .multipliedBy(sens.getCurrency(), sens.getSensitivity())); assertEquals(test.parameterSensitivity(sens), expected); } //------------------------------------------------------------------------- // proper end-to-end FD tests are elsewhere public void test_parameterSensitivity() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); ZeroRateSensitivity point = ZeroRateSensitivity.of(GBP, 1d, 1d); assertEquals(test.parameterSensitivity(point).size(), 1); } //------------------------------------------------------------------------- public void test_createParameterSensitivity() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); DoubleArray sensitivities = DoubleArray.of(0.12, 0.15); CurrencyParameterSensitivities sens = test.createParameterSensitivity(USD, sensitivities); assertEquals(sens.getSensitivities().get(0), CURVE.createParameterSensitivity(USD, sensitivities)); } //------------------------------------------------------------------------- public void test_withCurve() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE).withCurve(CURVE2); assertEquals(test.getCurve(), CURVE2); } //------------------------------------------------------------------------- public void coverage() { ZeroRateDiscountFactors test = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE); coverImmutableBean(test); ZeroRateDiscountFactors test2 = ZeroRateDiscountFactors.of(USD, DATE_VAL.plusDays(1), CURVE2); coverBeanEquals(test, test2); } }