/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.rate;
import static com.opengamma.strata.basics.currency.Currency.GBP;
import static com.opengamma.strata.basics.index.FxIndices.GBP_USD_WM;
import static com.opengamma.strata.basics.index.IborIndices.EUR_EURIBOR_3M;
import static com.opengamma.strata.basics.index.IborIndices.USD_LIBOR_3M;
import static com.opengamma.strata.basics.index.OvernightIndices.EUR_EONIA;
import static com.opengamma.strata.basics.index.OvernightIndices.USD_FED_FUND;
import static com.opengamma.strata.basics.index.PriceIndices.GB_RPI;
import static com.opengamma.strata.collect.TestHelper.date;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.time.LocalDate;
import java.time.YearMonth;
import org.testng.annotations.Test;
import com.google.common.base.Preconditions;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.currency.FxMatrix;
import com.opengamma.strata.basics.date.DayCount;
import com.opengamma.strata.basics.date.DayCounts;
import com.opengamma.strata.basics.index.FxIndexObservation;
import com.opengamma.strata.basics.index.IborIndexObservation;
import com.opengamma.strata.basics.index.OvernightIndexObservation;
import com.opengamma.strata.basics.index.PriceIndexObservation;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries;
import com.opengamma.strata.market.curve.Curve;
import com.opengamma.strata.market.curve.CurveMetadata;
import com.opengamma.strata.market.curve.Curves;
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;
import com.opengamma.strata.market.param.ParameterMetadata;
import com.opengamma.strata.market.param.UnitParameterSensitivity;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.ZeroRateSensitivity;
import com.opengamma.strata.pricer.datasets.StandardDataSets;
import com.opengamma.strata.pricer.fx.FxIndexSensitivity;
/**
* Tests related to {@link ImmutableRatesProvider} for the computation of curve parameters sensitivities.
*/
@Test
public class ImmutableRatesProviderParameterSensitivityTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final LocalDate VAL_DATE = LocalDate.of(2014, 1, 22);
private static final DayCount DAY_COUNT = DayCounts.ACT_ACT_ISDA;
private static final Currency USD = Currency.USD;
private static final Currency EUR = Currency.EUR;
private static final LocalDate DATE_1 = LocalDate.of(2015, 12, 21);
private static final LocalDate DATE_2 = LocalDate.of(2016, 1, 21);
private static final LocalDate DATE_3 = LocalDate.of(2016, 3, 21);
private static final double AMOUNT_1 = 1000.0;
private static final PointSensitivities POINT_ZERO_1 =
PointSensitivities.of(ZeroRateSensitivity.of(USD, DAY_COUNT.relativeYearFraction(VAL_DATE, DATE_1), AMOUNT_1));
private static final PointSensitivities POINT_ZERO_2 =
PointSensitivities.of(ZeroRateSensitivity.of(USD, DAY_COUNT.relativeYearFraction(VAL_DATE, DATE_2), AMOUNT_1));
private static final PointSensitivities POINT_ZERO_3 =
PointSensitivities.of(ZeroRateSensitivity.of(EUR, DAY_COUNT.relativeYearFraction(VAL_DATE, DATE_1), AMOUNT_1));
private static final PointSensitivities POINT_ZERO_4 =
PointSensitivities.of(ZeroRateSensitivity.of(EUR, DAY_COUNT.relativeYearFraction(VAL_DATE, DATE_1), USD, AMOUNT_1));
private static final PointSensitivities POINT_IBOR_1 =
PointSensitivities.of(
IborRateSensitivity.of(IborIndexObservation.of(USD_LIBOR_3M, DATE_1, REF_DATA), AMOUNT_1));
private static final PointSensitivities POINT_IBOR_2 =
PointSensitivities.of(
IborRateSensitivity.of(IborIndexObservation.of(USD_LIBOR_3M, DATE_3, REF_DATA), AMOUNT_1));
private static final PointSensitivities POINT_IBOR_3 =
PointSensitivities.of(
IborRateSensitivity.of(IborIndexObservation.of(USD_LIBOR_3M, DATE_1, REF_DATA), EUR, AMOUNT_1));
private static final PointSensitivities POINT_IBOR_4 =
PointSensitivities.of(
IborRateSensitivity.of(IborIndexObservation.of(EUR_EURIBOR_3M, DATE_1, REF_DATA), EUR, AMOUNT_1));
private static final PointSensitivities POINT_ON_1 =
PointSensitivities.of(
OvernightRateSensitivity.of(OvernightIndexObservation.of(USD_FED_FUND, DATE_1, REF_DATA), AMOUNT_1));
private static final PointSensitivities POINT_ON_2 =
PointSensitivities.of(
OvernightRateSensitivity.ofPeriod(OvernightIndexObservation.of(USD_FED_FUND, DATE_1, REF_DATA), DATE_2, USD, AMOUNT_1));
private static final PointSensitivities POINT_ON_3 =
PointSensitivities.of(
OvernightRateSensitivity.ofPeriod(OvernightIndexObservation.of(USD_FED_FUND, DATE_2, REF_DATA), DATE_3, USD, AMOUNT_1));
private static final PointSensitivities POINT_ON_4 =
PointSensitivities.of(
OvernightRateSensitivity.of(OvernightIndexObservation.of(EUR_EONIA, DATE_1, REF_DATA), AMOUNT_1));
private static final PointSensitivities[] POINTS = new PointSensitivities[] {
POINT_ZERO_1, POINT_ZERO_2, POINT_ZERO_3, POINT_ZERO_4,
POINT_IBOR_1, POINT_IBOR_2, POINT_IBOR_3, POINT_IBOR_4,
POINT_ON_1, POINT_ON_2, POINT_ON_3, POINT_ON_4};
private static final PointSensitivities POINT =
POINT_ZERO_1.combinedWith(POINT_ZERO_2).combinedWith(POINT_ZERO_3).combinedWith(POINT_ZERO_4)
.combinedWith(POINT_IBOR_1).combinedWith(POINT_IBOR_2).combinedWith(POINT_IBOR_3).combinedWith(POINT_IBOR_4)
.combinedWith(POINT_ON_1).combinedWith(POINT_ON_2).combinedWith(POINT_ON_3).combinedWith(POINT_ON_4);
private static final FxMatrix FX_MATRIX = FxMatrix.of(GBP, USD, 1.60);
// rates provider
private static RatesProvider PROVIDER = StandardDataSets.providerUsdEurDscL3();
private static final double TOLERANCE_SENSI = 1.0E-8;
//-------------------------------------------------------------------------
public void pointToParameterMultiple() {
CurrencyParameterSensitivities psComputed = PROVIDER.parameterSensitivity(POINT);
assertEquals(psComputed.getSensitivities().size(), 6);
CurrencyParameterSensitivities psExpected = CurrencyParameterSensitivities.empty();
for (int i = 0; i < POINTS.length; i++) {
psExpected = psExpected.combinedWith(PROVIDER.parameterSensitivity(POINTS[i]));
}
assertTrue(psComputed.equalWithTolerance(psExpected, TOLERANCE_SENSI));
}
//-------------------------------------------------------------------------
private static final double GBP_DSC = 0.99d;
private static final double USD_DSC = 0.95d;
private static final double EPS_FD = 1.0e-7;
private static final Curve DISCOUNT_CURVE_GBP = new ConstantDiscountFactorCurve("GBP-Discount", GBP_DSC);
private static final Curve DISCOUNT_CURVE_GBP_UP = new ConstantDiscountFactorCurve("GBP-DiscountUp", GBP_DSC + EPS_FD);
private static final Curve DISCOUNT_CURVE_GBP_DOWN = new ConstantDiscountFactorCurve("GBP-DiscountDown", GBP_DSC - EPS_FD);
private static final Curve DISCOUNT_CURVE_USD = new ConstantDiscountFactorCurve("USD-Discount", USD_DSC);
private static final Curve DISCOUNT_CURVE_USD_UP = new ConstantDiscountFactorCurve("USD-DiscountUp", USD_DSC + EPS_FD);
private static final Curve DISCOUNT_CURVE_USD_DOWN = new ConstantDiscountFactorCurve("USD-DiscountDown", USD_DSC - EPS_FD);
public void pointAndParameterFx() {
ImmutableRatesProvider test = ImmutableRatesProvider.builder(VAL_DATE)
.fxRateProvider(FX_MATRIX)
.discountCurve(GBP, DISCOUNT_CURVE_GBP)
.discountCurve(USD, DISCOUNT_CURVE_USD)
.build();
ImmutableRatesProvider test_gbp_up = ImmutableRatesProvider.builder(VAL_DATE)
.fxRateProvider(FX_MATRIX)
.discountCurve(GBP, DISCOUNT_CURVE_GBP_UP)
.discountCurve(USD, DISCOUNT_CURVE_USD)
.build();
ImmutableRatesProvider test_gbp_dw = ImmutableRatesProvider.builder(VAL_DATE)
.fxRateProvider(FX_MATRIX)
.discountCurve(GBP, DISCOUNT_CURVE_GBP_DOWN)
.discountCurve(USD, DISCOUNT_CURVE_USD)
.build();
ImmutableRatesProvider test_usd_up = ImmutableRatesProvider.builder(VAL_DATE)
.fxRateProvider(FX_MATRIX)
.discountCurve(GBP, DISCOUNT_CURVE_GBP)
.discountCurve(USD, DISCOUNT_CURVE_USD_UP)
.build();
ImmutableRatesProvider test_usd_dw = ImmutableRatesProvider.builder(VAL_DATE)
.fxRateProvider(FX_MATRIX)
.discountCurve(GBP, DISCOUNT_CURVE_GBP)
.discountCurve(USD, DISCOUNT_CURVE_USD_DOWN)
.build();
LocalDate matuirtyDate = GBP_USD_WM.calculateMaturityFromFixing(VAL_DATE, REF_DATA);
double maturityTime = DAY_COUNT.relativeYearFraction(VAL_DATE, matuirtyDate);
// GBP based
FxIndexObservation obs = FxIndexObservation.of(GBP_USD_WM, VAL_DATE, REF_DATA);
PointSensitivityBuilder sensiBuildCmpGBP = test.fxIndexRates(GBP_USD_WM).ratePointSensitivity(obs, GBP);
FxIndexSensitivity sensiBuildExpGBP = FxIndexSensitivity.of(obs, GBP, USD, 1.0);
assertTrue(sensiBuildCmpGBP.equals(sensiBuildExpGBP));
double sense_gbp1 = 0.5 * (test_gbp_up.fxIndexRates(GBP_USD_WM).rate(obs, GBP) -
test_gbp_dw.fxIndexRates(GBP_USD_WM).rate(obs, GBP)) / EPS_FD * (-maturityTime * GBP_DSC);
double sense_usd1 = 0.5 * (test_usd_up.fxIndexRates(GBP_USD_WM).rate(obs, GBP) -
test_usd_dw.fxIndexRates(GBP_USD_WM).rate(obs, GBP)) / EPS_FD * (-maturityTime * USD_DSC);
PointSensitivityBuilder sensiBuildDecGBP = ZeroRateSensitivity.of(GBP, maturityTime, USD, sense_gbp1);
sensiBuildDecGBP = sensiBuildDecGBP.combinedWith(ZeroRateSensitivity.of(USD, maturityTime, USD, sense_usd1));
CurrencyParameterSensitivities paramSensiCmpGBP = test.parameterSensitivity(sensiBuildCmpGBP.build().normalized());
CurrencyParameterSensitivities paramSensiExpGBP = test.parameterSensitivity(sensiBuildDecGBP.build().normalized());
assertTrue(paramSensiCmpGBP.equalWithTolerance(paramSensiExpGBP, EPS_FD));
// USD based
PointSensitivityBuilder sensiBuildCmpUSD = test.fxIndexRates(GBP_USD_WM).ratePointSensitivity(obs, USD);
FxIndexSensitivity sensiBuildExpUSD = FxIndexSensitivity.of(obs, USD, GBP, 1.0);
assertTrue(sensiBuildCmpUSD.equals(sensiBuildExpUSD));
double sense_gbp2 = 0.5 * (test_gbp_up.fxIndexRates(GBP_USD_WM).rate(obs, USD) -
test_gbp_dw.fxIndexRates(GBP_USD_WM).rate(obs, USD)) / EPS_FD * (-maturityTime * GBP_DSC);
double sense_usd2 = 0.5 * (test_usd_up.fxIndexRates(GBP_USD_WM).rate(obs, USD) -
test_usd_dw.fxIndexRates(GBP_USD_WM).rate(obs, USD)) / EPS_FD * (-maturityTime * USD_DSC);
PointSensitivityBuilder sensiBuildDecUSD = ZeroRateSensitivity.of(GBP, maturityTime, GBP, sense_gbp2);
sensiBuildDecUSD = sensiBuildDecUSD.combinedWith(ZeroRateSensitivity.of(USD, maturityTime, GBP, sense_usd2));
CurrencyParameterSensitivities paramSensiCmpUSD = test.parameterSensitivity(sensiBuildCmpUSD.build().normalized());
CurrencyParameterSensitivities paramSensiExpUSD = test.parameterSensitivity(sensiBuildDecUSD.build().normalized());
assertTrue(paramSensiCmpUSD.equalWithTolerance(paramSensiExpUSD, EPS_FD));
}
public void pointAndParameterPriceIndex() {
double eps = 1.0e-13;
LocalDate valuationDate = LocalDate.of(2014, 1, 22);
DoubleArray x = DoubleArray.of(0.5, 1.0, 2.0);
DoubleArray y = DoubleArray.of(224.2, 262.6, 277.5);
CurveInterpolator interp = CurveInterpolators.NATURAL_CUBIC_SPLINE;
String curveName = "GB_RPI_CURVE";
InterpolatedNodalCurve interpCurve = InterpolatedNodalCurve.of(Curves.prices(curveName), x, y, interp);
ImmutableRatesProvider provider = ImmutableRatesProvider.builder(VAL_DATE)
.priceIndexCurve(GB_RPI, interpCurve)
.timeSeries(GB_RPI, LocalDateDoubleTimeSeries.of(date(2013, 11, 30), 200))
.build();
double pointSensiValue = 2.5;
YearMonth refMonth = YearMonth.from(valuationDate.plusMonths(9));
InflationRateSensitivity pointSensi = InflationRateSensitivity.of(
PriceIndexObservation.of(GB_RPI, refMonth), pointSensiValue);
CurrencyParameterSensitivities computed = provider.parameterSensitivity(pointSensi.build());
DoubleArray sensiComputed = computed.getSensitivities().get(0).getSensitivity();
InflationRateSensitivity pointSensi1 = InflationRateSensitivity.of(PriceIndexObservation.of(GB_RPI, refMonth), 1);
DoubleArray sensiExpectedUnit =
provider.priceIndexValues(GB_RPI).parameterSensitivity(pointSensi1).getSensitivities().get(0).getSensitivity();
assertTrue(sensiComputed.equalWithTolerance(sensiExpectedUnit.multipliedBy(pointSensiValue), eps));
}
//-------------------------------------------------------------------------
// a curve that produces a constant discount factor
static class ConstantDiscountFactorCurve implements Curve {
private CurveMetadata metadata;
private double discountFactor;
public ConstantDiscountFactorCurve(String name, double discountFactor) {
this.metadata = Curves.zeroRates(name, DAY_COUNT);
this.discountFactor = discountFactor;
}
@Override
public CurveMetadata getMetadata() {
return metadata;
}
@Override
public Curve withMetadata(CurveMetadata metadata) {
return this;
}
//-------------------------------------------------------------------------
@Override
public int getParameterCount() {
return 1;
}
@Override
public double getParameter(int parameterIndex) {
Preconditions.checkElementIndex(parameterIndex, 1);
return discountFactor;
}
@Override
public ParameterMetadata getParameterMetadata(int parameterIndex) {
return ParameterMetadata.empty();
}
@Override
public ConstantDiscountFactorCurve withParameter(int parameterIndex, double newValue) {
return new ConstantDiscountFactorCurve(metadata.getCurveName().toString(), newValue);
}
//-------------------------------------------------------------------------
@Override
public double yValue(double x) {
return -Math.log(discountFactor) / x;
}
@Override
public UnitParameterSensitivity yValueParameterSensitivity(double x) {
return createParameterSensitivity(DoubleArray.of(1d));
}
@Override
public double firstDerivative(double x) {
throw new UnsupportedOperationException();
}
}
}