/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.fx;
import static com.opengamma.strata.basics.currency.Currency.EUR;
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.basics.index.FxIndices.EUR_GBP_ECB;
import static com.opengamma.strata.basics.index.FxIndices.GBP_USD_WM;
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 org.testng.Assert.assertEquals;
import java.time.LocalDate;
import java.util.Optional;
import org.testng.annotations.Test;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.CurrencyPair;
import com.opengamma.strata.basics.currency.FxRate;
import com.opengamma.strata.basics.index.FxIndexObservation;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries;
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.InterpolatedNodalCurve;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolator;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolators;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.ZeroRateDiscountFactors;
/**
* Test {@link ForwardFxIndexRates}.
*/
@Test
public class ForwardFxIndexRatesTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final LocalDate DATE_VAL = date(2015, 6, 4);
private static final LocalDate DATE_BEFORE = date(2015, 6, 3);
private static final LocalDate DATE_AFTER = date(2015, 7, 30);
private static final FxIndexObservation OBS_VAL = FxIndexObservation.of(GBP_USD_WM, DATE_VAL, REF_DATA);
private static final FxIndexObservation OBS_BEFORE = FxIndexObservation.of(GBP_USD_WM, DATE_BEFORE, REF_DATA);
private static final FxIndexObservation OBS_AFTER = FxIndexObservation.of(GBP_USD_WM, DATE_AFTER, REF_DATA);
private static final FxIndexObservation OBS_EUR_VAL = FxIndexObservation.of(EUR_GBP_ECB, DATE_VAL, REF_DATA);
private static final CurrencyPair PAIR_GBP_USD = CurrencyPair.of(GBP, USD);
private static final CurrencyPair PAIR_USD_GBP = CurrencyPair.of(USD, GBP);
private static final CurrencyPair PAIR_EUR_GBP = CurrencyPair.of(EUR, GBP);
private static final FxRate FX_RATE = FxRate.of(GBP, USD, 1.5d);
private static final FxRate FX_RATE_EUR_GBP = FxRate.of(EUR, GBP, 0.7d);
private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR;
private static final CurveMetadata METADATA1 = Curves.zeroRates("TestCurve", ACT_365F);
private static final CurveMetadata METADATA2 = Curves.zeroRates("TestCurveUSD", ACT_365F);
private static final InterpolatedNodalCurve CURVE1 =
InterpolatedNodalCurve.of(METADATA1, DoubleArray.of(0, 10), DoubleArray.of(0.01, 0.02), INTERPOLATOR);
private static final InterpolatedNodalCurve CURVE2 =
InterpolatedNodalCurve.of(METADATA2, DoubleArray.of(0, 10), DoubleArray.of(0.015, 0.025), INTERPOLATOR);
private static final ZeroRateDiscountFactors DFCURVE_GBP = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE1);
private static final ZeroRateDiscountFactors DFCURVE_USD = ZeroRateDiscountFactors.of(USD, DATE_VAL, CURVE2);
private static final ZeroRateDiscountFactors DFCURVE_EUR = ZeroRateDiscountFactors.of(EUR, DATE_VAL, CURVE2);
private static final double RATE_BEFORE = 0.013d;
private static final double RATE_VAL = 0.014d;
private static final LocalDateDoubleTimeSeries SERIES = LocalDateDoubleTimeSeries.builder()
.put(DATE_BEFORE, RATE_BEFORE)
.put(DATE_VAL, RATE_VAL)
.build();
private static final LocalDateDoubleTimeSeries SERIES_MINIMAL = LocalDateDoubleTimeSeries.of(DATE_VAL, RATE_VAL);
private static final LocalDateDoubleTimeSeries SERIES_EMPTY = LocalDateDoubleTimeSeries.empty();
private static final FxForwardRates FWD_RATES = DiscountFxForwardRates.of(PAIR_GBP_USD, FX_RATE, DFCURVE_GBP, DFCURVE_USD);
private static final FxForwardRates FWD_RATES_USD_GBP =
DiscountFxForwardRates.of(PAIR_USD_GBP, FX_RATE.inverse(), DFCURVE_USD, DFCURVE_GBP);
private static final FxForwardRates FWD_RATES_EUR_GBP =
DiscountFxForwardRates.of(PAIR_EUR_GBP, FX_RATE_EUR_GBP, DFCURVE_EUR, DFCURVE_GBP);
//-------------------------------------------------------------------------
public void test_of_withoutFixings() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES);
assertEquals(test.getIndex(), GBP_USD_WM);
assertEquals(test.getValuationDate(), DATE_VAL);
assertEquals(test.getFixings(), SERIES_EMPTY);
assertEquals(test.getFxForwardRates(), FWD_RATES);
assertEquals(test.findData(CURVE1.getName()), Optional.of(CURVE1));
assertEquals(test.findData(CURVE2.getName()), Optional.of(CURVE2));
assertEquals(test.findData(CurveName.of("Rubbish")), Optional.empty());
assertEquals(test.getParameterCount(), FWD_RATES.getParameterCount());
assertEquals(test.getParameter(0), FWD_RATES.getParameter(0));
assertEquals(test.getParameterMetadata(0), FWD_RATES.getParameterMetadata(0));
assertEquals(test.withParameter(0, 1d).getFxForwardRates(), FWD_RATES.withParameter(0, 1d));
assertEquals(
test.withPerturbation((i, v, m) -> v + 1d).getFxForwardRates(),
FWD_RATES.withPerturbation((i, v, m) -> v + 1d));
}
public void test_of_withFixings() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
assertEquals(test.getIndex(), GBP_USD_WM);
assertEquals(test.getValuationDate(), DATE_VAL);
assertEquals(test.getFixings(), SERIES);
assertEquals(test.getFxForwardRates(), FWD_RATES);
}
public void test_of_nonMatchingCurrency() {
assertThrowsIllegalArg(() -> ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES_USD_GBP, SERIES));
assertThrowsIllegalArg(() -> ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES_EUR_GBP, SERIES));
}
//-------------------------------------------------------------------------
public void test_rate_beforeValuation_fixing() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
assertEquals(test.rate(OBS_BEFORE, GBP), RATE_BEFORE);
assertEquals(test.rate(OBS_BEFORE, USD), 1d / RATE_BEFORE);
}
public void test_rate_beforeValuation_noFixing_emptySeries() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES_EMPTY);
assertThrowsIllegalArg(() -> test.rate(OBS_BEFORE, GBP));
}
public void test_rate_beforeValuation_noFixing_notEmptySeries() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES_MINIMAL);
assertThrowsIllegalArg(() -> test.rate(OBS_BEFORE, GBP));
}
public void test_rate_onValuation_fixing() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
assertEquals(test.rate(OBS_VAL, GBP), RATE_VAL);
assertEquals(test.rate(OBS_VAL, USD), 1d / RATE_VAL);
}
public void test_rate_onValuation_noFixing() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES_EMPTY);
LocalDate maturityDate = GBP_USD_WM.calculateMaturityFromFixing(DATE_VAL, REF_DATA);
double dfCcyBaseAtMaturity = DFCURVE_GBP.discountFactor(maturityDate);
double dfCcyCounterAtMaturity = DFCURVE_USD.discountFactor(maturityDate);
double expected = FX_RATE.fxRate(GBP, USD) * (dfCcyBaseAtMaturity / dfCcyCounterAtMaturity);
assertEquals(test.rate(OBS_VAL, GBP), expected, 1e-8);
assertEquals(test.rate(OBS_VAL, USD), 1d / expected, 1e-8);
}
public void test_rate_afterValuation() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
LocalDate maturityDate = GBP_USD_WM.calculateMaturityFromFixing(DATE_AFTER, REF_DATA);
double dfCcyBaseAtMaturity = DFCURVE_GBP.discountFactor(maturityDate);
double dfCcyCounterAtMaturity = DFCURVE_USD.discountFactor(maturityDate);
double expected = FX_RATE.fxRate(GBP, USD) * (dfCcyBaseAtMaturity / dfCcyCounterAtMaturity);
assertEquals(test.rate(OBS_AFTER, GBP), expected, 1e-8);
assertEquals(test.rate(OBS_AFTER, USD), 1d / expected, 1e-8);
}
public void test_rate_nonMatchingCurrency() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
assertThrowsIllegalArg(() -> test.rate(OBS_EUR_VAL, EUR));
}
//-------------------------------------------------------------------------
public void test_ratePointSensitivity_fixing() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
assertEquals(test.ratePointSensitivity(OBS_BEFORE, GBP), PointSensitivityBuilder.none());
assertEquals(test.ratePointSensitivity(OBS_VAL, GBP), PointSensitivityBuilder.none());
}
public void test_ratePointSensitivity_onValuation_noFixing() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES_EMPTY);
assertEquals(test.ratePointSensitivity(OBS_VAL, GBP), FxIndexSensitivity.of(OBS_VAL, GBP, 1d));
assertEquals(test.ratePointSensitivity(OBS_VAL, USD), FxIndexSensitivity.of(OBS_VAL, USD, 1d));
}
public void test_ratePointSensitivity_afterValuation() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
assertEquals(test.ratePointSensitivity(OBS_AFTER, GBP), FxIndexSensitivity.of(OBS_AFTER, GBP, 1d));
assertEquals(test.ratePointSensitivity(OBS_AFTER, USD), FxIndexSensitivity.of(OBS_AFTER, USD, 1d));
}
public void test_ratePointSensitivity_nonMatchingCurrency() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
assertThrowsIllegalArg(() -> test.ratePointSensitivity(OBS_EUR_VAL, EUR));
}
//-------------------------------------------------------------------------
//proper end-to-end tests are elsewhere
public void test_parameterSensitivity() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
FxIndexSensitivity point = FxIndexSensitivity.of(OBS_VAL, GBP, 1d);
assertEquals(test.parameterSensitivity(point).size(), 2);
FxIndexSensitivity point2 = FxIndexSensitivity.of(OBS_VAL, USD, 1d);
assertEquals(test.parameterSensitivity(point2).size(), 2);
}
//-------------------------------------------------------------------------
public void coverage() {
ForwardFxIndexRates test = ForwardFxIndexRates.of(GBP_USD_WM, FWD_RATES, SERIES);
coverImmutableBean(test);
ForwardFxIndexRates test2 = ForwardFxIndexRates.of(EUR_GBP_ECB, FWD_RATES_EUR_GBP, SERIES_MINIMAL);
coverBeanEquals(test, test2);
}
}