/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.impl.rate;
import static com.opengamma.strata.basics.date.DayCounts.ACT_ACT_ISDA;
import static com.opengamma.strata.basics.index.OvernightIndices.CHF_TOIS;
import static com.opengamma.strata.basics.index.OvernightIndices.GBP_SONIA;
import static com.opengamma.strata.basics.index.OvernightIndices.USD_FED_FUND;
import static com.opengamma.strata.collect.TestHelper.date;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.testng.annotations.Test;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.basics.index.OvernightIndex;
import com.opengamma.strata.basics.index.OvernightIndexObservation;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeriesBuilder;
import com.opengamma.strata.market.curve.Curve;
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.explain.ExplainKey;
import com.opengamma.strata.market.explain.ExplainMap;
import com.opengamma.strata.market.explain.ExplainMapBuilder;
import com.opengamma.strata.market.param.CurrencyParameterSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.rate.ImmutableRatesProvider;
import com.opengamma.strata.pricer.rate.OvernightIndexRates;
import com.opengamma.strata.pricer.rate.OvernightRateSensitivity;
import com.opengamma.strata.pricer.rate.SimpleRatesProvider;
import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator;
import com.opengamma.strata.product.rate.OvernightAveragedRateComputation;
/**
* Test {@link ForwardOvernightAveragedRateComputationFn}.
*/
@Test
public class ForwardOvernightAveragedRateComputationFnTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final LocalDate DUMMY_ACCRUAL_START_DATE = date(2015, 1, 1); // Accrual dates irrelevant for the rate
private static final LocalDate DUMMY_ACCRUAL_END_DATE = date(2015, 1, 1); // Accrual dates irrelevant for the rate
private static final LocalDate START_DATE = date(2015, 1, 8);
private static final LocalDate END_DATE = date(2015, 1, 15); // 1w only to decrease data
private static final LocalDate[] FIXING_DATES = new LocalDate[] {
date(2015, 1, 7), date(2015, 1, 8), date(2015, 1, 9),
date(2015, 1, 12), date(2015, 1, 13), date(2015, 1, 14), date(2015, 1, 15)};
private static final OvernightIndexObservation[] USD_OBS = new OvernightIndexObservation[] {
OvernightIndexObservation.of(USD_FED_FUND, date(2015, 1, 7), REF_DATA),
OvernightIndexObservation.of(USD_FED_FUND, date(2015, 1, 8), REF_DATA),
OvernightIndexObservation.of(USD_FED_FUND, date(2015, 1, 9), REF_DATA),
OvernightIndexObservation.of(USD_FED_FUND, date(2015, 1, 12), REF_DATA),
OvernightIndexObservation.of(USD_FED_FUND, date(2015, 1, 13), REF_DATA),
OvernightIndexObservation.of(USD_FED_FUND, date(2015, 1, 14), REF_DATA),
OvernightIndexObservation.of(USD_FED_FUND, date(2015, 1, 15), REF_DATA)};
private static final OvernightIndexObservation[] GBP_OBS = new OvernightIndexObservation[] {
OvernightIndexObservation.of(GBP_SONIA, date(2015, 1, 7), REF_DATA),
OvernightIndexObservation.of(GBP_SONIA, date(2015, 1, 8), REF_DATA),
OvernightIndexObservation.of(GBP_SONIA, date(2015, 1, 9), REF_DATA),
OvernightIndexObservation.of(GBP_SONIA, date(2015, 1, 12), REF_DATA),
OvernightIndexObservation.of(GBP_SONIA, date(2015, 1, 13), REF_DATA),
OvernightIndexObservation.of(GBP_SONIA, date(2015, 1, 14), REF_DATA),
OvernightIndexObservation.of(GBP_SONIA, date(2015, 1, 15), REF_DATA)};
private static final OvernightIndexObservation[] CHF_OBS = new OvernightIndexObservation[] {
OvernightIndexObservation.of(CHF_TOIS, date(2015, 1, 7), REF_DATA),
OvernightIndexObservation.of(CHF_TOIS, date(2015, 1, 8), REF_DATA),
OvernightIndexObservation.of(CHF_TOIS, date(2015, 1, 9), REF_DATA),
OvernightIndexObservation.of(CHF_TOIS, date(2015, 1, 12), REF_DATA),
OvernightIndexObservation.of(CHF_TOIS, date(2015, 1, 13), REF_DATA),
OvernightIndexObservation.of(CHF_TOIS, date(2015, 1, 14), REF_DATA),
OvernightIndexObservation.of(CHF_TOIS, date(2015, 1, 15), REF_DATA)};
private static final double[] FIXING_RATES = {
0.0012, 0.0023, 0.0034,
0.0045, 0.0056, 0.0067, 0.0078};
private static final double TOLERANCE_RATE = 1.0E-10;
private static final double EPS_FD = 1.0E-7;
//-------------------------------------------------------------------------
/** Test for the case where publication lag=1, effective offset=0 (USD conventions) and no cutoff period. */
public void rateFedFundNoCutOff() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(USD_FED_FUND);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < USD_OBS.length; i++) {
when(mockRates.rate(USD_OBS[i])).thenReturn(FIXING_RATES[i]);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(USD_FED_FUND, START_DATE, END_DATE, 0, REF_DATA);
// Accrual dates = fixing dates
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
double accrualFactorTotal = 0.0d;
double accruedRate = 0.0d;
int indexLast = 5; // Fixing in the observation period are from 1 to 5 (inclusive)
for (int i = 1; i <= indexLast; i++) {
LocalDate endDate = USD_OBS[i].getMaturityDate();
double af = USD_FED_FUND.getDayCount().yearFraction(FIXING_DATES[i], endDate);
accrualFactorTotal += af;
accruedRate += FIXING_RATES[i] * af;
}
double rateExpected = accruedRate / accrualFactorTotal;
double rateComputed = obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
assertEquals(rateExpected, rateComputed, TOLERANCE_RATE);
// explain
ExplainMapBuilder builder = ExplainMap.builder();
double explainedRate = obsFn.explainRate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv, builder);
assertEquals(explainedRate, rateExpected, TOLERANCE_RATE);
ExplainMap built = builder.build();
assertEquals(built.get(ExplainKey.OBSERVATIONS).isPresent(), false);
assertEquals(built.get(ExplainKey.COMBINED_RATE).get().doubleValue(), rateExpected, TOLERANCE_RATE);
}
/** Test against FD approximation for the case where publication lag=1, effective offset=0 (USD conventions) and
* no cutoff period. Note that all the rates are bumped here, i.e., all the rates are treated as forward rates.*/
public void rateFedFundNoCutOffSensitivity() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(USD_FED_FUND);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < USD_OBS.length; i++) {
when(mockRates.rate(USD_OBS[i])).thenReturn(FIXING_RATES[i]);
LocalDate fixingEndDate = USD_OBS[i].getMaturityDate();
OvernightRateSensitivity sensitivity = OvernightRateSensitivity.ofPeriod(USD_OBS[i],
fixingEndDate, USD_FED_FUND.getCurrency(), 1d);
when(mockRates.ratePointSensitivity(USD_OBS[i])).thenReturn(sensitivity);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(USD_FED_FUND, START_DATE, END_DATE, 0, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
PointSensitivityBuilder sensitivityBuilderComputed = obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE,
DUMMY_ACCRUAL_END_DATE, simpleProv);
PointSensitivities sensitivityComputed = sensitivityBuilderComputed.build().normalized();
Double[] sensitivityExpected = computedSensitivityFD(ro, USD_FED_FUND, USD_OBS);
assertEquals(sensitivityComputed.getSensitivities().size(), sensitivityExpected.length);
for (int i = 0; i < sensitivityExpected.length; ++i) {
assertEquals(sensitivityComputed.getSensitivities().get(i).getSensitivity(), sensitivityExpected[i], EPS_FD);
}
}
/** Test for the case where publication lag=1, effective offset=0 (USD conventions) and cutoff=2 (FedFund swaps). */
public void rateFedFund() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(USD_FED_FUND);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < USD_OBS.length; i++) {
when(mockRates.rate(USD_OBS[i])).thenReturn(FIXING_RATES[i]);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(USD_FED_FUND, START_DATE, END_DATE, 2, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
double accrualFactorTotal = 0.0d;
double accruedRate = 0.0d;
int indexLast = 5; // Fixing in the observation period are from 1 to 5 (inclusive), but last is modified by cut-off
for (int i = 1; i <= indexLast - 1; i++) {
LocalDate endDate = USD_OBS[i].getMaturityDate();
double af = USD_FED_FUND.getDayCount().yearFraction(FIXING_DATES[i], endDate);
accrualFactorTotal += af;
accruedRate += FIXING_RATES[i] * af;
}
// CutOff
LocalDate endDate = USD_OBS[indexLast].getMaturityDate();
double af = USD_FED_FUND.getDayCount().yearFraction(FIXING_DATES[indexLast], endDate);
accrualFactorTotal += af;
accruedRate += FIXING_RATES[indexLast - 1] * af;
double rateExpected = accruedRate / accrualFactorTotal;
double rateComputed = obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
assertEquals(rateExpected, rateComputed, TOLERANCE_RATE);
}
/** Test against FD approximation for the case where publication lag=1, effective offset=0 (USD conventions) and
* cutoff=2 (FedFund swaps).
* Note that all the rates are bumped here, i.e., all the rates are treated as forward rates. */
public void rateFedFundSensitivity() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(USD_FED_FUND);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < USD_OBS.length; i++) {
when(mockRates.rate(USD_OBS[i])).thenReturn(FIXING_RATES[i]);
OvernightRateSensitivity sensitivity = OvernightRateSensitivity.of(USD_OBS[i], USD_FED_FUND.getCurrency(), 1d);
when(mockRates.ratePointSensitivity(USD_OBS[i])).thenReturn(sensitivity);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(USD_FED_FUND, START_DATE, END_DATE, 2, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
PointSensitivityBuilder sensitivityBuilderComputed = obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE,
DUMMY_ACCRUAL_END_DATE, simpleProv);
PointSensitivities sensitivityComputed = sensitivityBuilderComputed.build().normalized();
Double[] sensitivityExpected = computedSensitivityFD(ro, USD_FED_FUND, USD_OBS);
assertEquals(sensitivityComputed.getSensitivities().size(), sensitivityExpected.length);
for (int i = 0; i < sensitivityExpected.length; ++i) {
assertEquals(sensitivityComputed.getSensitivities().get(i).getSensitivity(), sensitivityExpected[i], EPS_FD);
}
}
/**
* Test for the case where publication lag=0, effective offset=1 (CHF conventions) and no cutoff period.
* The arithmetic average coupons are used mainly in USD. This test is more for completeness than a real case.
*/
public void rateChfNoCutOff() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(CHF_TOIS);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < CHF_OBS.length; i++) {
when(mockRates.rate(CHF_OBS[i])).thenReturn(FIXING_RATES[i]);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(CHF_TOIS, START_DATE, END_DATE, 0, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
double accrualFactorTotal = 0.0d;
double accruedRate = 0.0d;
int indexLast = 5; // Fixing in the observation period are from 0 to 4 (inclusive)
for (int i = 0; i < indexLast; i++) {
LocalDate startDate = CHF_OBS[i].getEffectiveDate();
LocalDate endDate = CHF_OBS[i].getMaturityDate();
double af = CHF_TOIS.getDayCount().yearFraction(startDate, endDate);
accrualFactorTotal += af;
accruedRate += FIXING_RATES[i] * af;
}
double rateExpected = accruedRate / accrualFactorTotal;
double rateComputed = obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
assertEquals(rateExpected, rateComputed, TOLERANCE_RATE);
}
/**
* Test for the case where publication lag=0, effective offset=1 (CHF conventions) and no cutoff period.
* The arithmetic average coupons are used mainly in USD. This test is more for completeness than a real case.
*/
public void rateChfNoCutOffSensitivity() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(CHF_TOIS);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < CHF_OBS.length; i++) {
when(mockRates.rate(CHF_OBS[i])).thenReturn(FIXING_RATES[i]);
OvernightRateSensitivity sensitivity = OvernightRateSensitivity.of(CHF_OBS[i], CHF_TOIS.getCurrency(), 1d);
when(mockRates.ratePointSensitivity(CHF_OBS[i])).thenReturn(sensitivity);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(CHF_TOIS, START_DATE, END_DATE, 0, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
PointSensitivityBuilder sensitivityBuilderComputed = obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE,
DUMMY_ACCRUAL_END_DATE, simpleProv);
PointSensitivities sensitivityComputed = sensitivityBuilderComputed.build().normalized();
Double[] sensitivityExpected = computedSensitivityFD(ro, CHF_TOIS, CHF_OBS);
assertEquals(sensitivityComputed.getSensitivities().size(), sensitivityExpected.length);
for (int i = 0; i < sensitivityExpected.length; ++i) {
assertEquals(sensitivityComputed.getSensitivities().get(i).getSensitivity(), sensitivityExpected[i], EPS_FD);
}
}
/** Test for the case where publication lag=0, effective offset=0 (GBP conventions) and no cutoff period.
* The arithmetic average coupons are used mainly in USD. This test is more for completeness than a real case. */
public void rateGbpNoCutOff() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(GBP_SONIA);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < GBP_OBS.length; i++) {
when(mockRates.rate(GBP_OBS[i])).thenReturn(FIXING_RATES[i]);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(GBP_SONIA, START_DATE, END_DATE, 0, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
double accrualFactorTotal = 0.0d;
double accruedRate = 0.0d;
int indexLast = 5; // Fixing in the observation period are from 1 to 5 (inclusive)
for (int i = 1; i <= indexLast; i++) {
LocalDate startDate = GBP_OBS[i].getEffectiveDate();
LocalDate endDate = GBP_OBS[i].getMaturityDate();
double af = GBP_SONIA.getDayCount().yearFraction(startDate, endDate);
accrualFactorTotal += af;
accruedRate += FIXING_RATES[i] * af;
}
double rateExpected = accruedRate / accrualFactorTotal;
double rateComputed = obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
assertEquals(rateExpected, rateComputed, TOLERANCE_RATE);
}
/** Test for the case where publication lag=0, effective offset=0 (GBP conventions) and no cutoff period.
* The arithmetic average coupons are used mainly in USD. This test is more for completeness than a real case. */
public void rateGbpNoCutOffSensitivity() {
OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
when(mockRates.getIndex()).thenReturn(GBP_SONIA);
SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);
for (int i = 0; i < GBP_OBS.length; i++) {
when(mockRates.rate(GBP_OBS[i])).thenReturn(FIXING_RATES[i]);
OvernightRateSensitivity sensitivity = OvernightRateSensitivity.of(GBP_OBS[i], GBP_SONIA.getCurrency(), 1d);
when(mockRates.ratePointSensitivity(GBP_OBS[i])).thenReturn(sensitivity);
}
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(GBP_SONIA, START_DATE, END_DATE, 0, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
PointSensitivityBuilder sensitivityBuilderComputed = obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE,
DUMMY_ACCRUAL_END_DATE, simpleProv);
PointSensitivities sensitivityComputed = sensitivityBuilderComputed.build().normalized();
Double[] sensitivityExpected = computedSensitivityFD(ro, GBP_SONIA, GBP_OBS);
assertEquals(sensitivityComputed.getSensitivities().size(), sensitivityExpected.length);
for (int i = 0; i < sensitivityExpected.length; ++i) {
assertEquals(sensitivityComputed.getSensitivities().get(i).getSensitivity(), sensitivityExpected[i], EPS_FD);
}
}
//-------------------------------------------------------------------------
private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.DOUBLE_QUADRATIC;
private static final LocalDateDoubleTimeSeries TIME_SERIES;
static {
LocalDateDoubleTimeSeriesBuilder builder = LocalDateDoubleTimeSeries.builder();
for (int i = 0; i < FIXING_DATES.length; i++) {
builder.put(FIXING_DATES[i], FIXING_RATES[i]);
}
TIME_SERIES = builder.build();
}
private static final RatesFiniteDifferenceSensitivityCalculator CAL_FD =
new RatesFiniteDifferenceSensitivityCalculator(EPS_FD);
/** Test parameter sensitivity with finite difference sensitivity calculator. Two days cutoff period. */
public void rateFedFundTwoDaysCutoffParameterSensitivity() {
LocalDate[] valuationDate = {date(2015, 1, 1), date(2015, 1, 8)};
DoubleArray time = DoubleArray.of(0.0, 0.5, 1.0, 2.0, 5.0, 10.0);
DoubleArray rate = DoubleArray.of(0.0100, 0.0110, 0.0115, 0.0130, 0.0135, 0.0135);
for (int loopvaldate = 0; loopvaldate < 2; loopvaldate++) {
Curve onCurve = InterpolatedNodalCurve.of(
Curves.zeroRates("ON", ACT_ACT_ISDA), time, rate, INTERPOLATOR);
ImmutableRatesProvider prov = ImmutableRatesProvider.builder(valuationDate[loopvaldate])
.overnightIndexCurve(USD_FED_FUND, onCurve, TIME_SERIES)
.build();
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(USD_FED_FUND, START_DATE, END_DATE, 2, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
PointSensitivityBuilder sensitivityBuilderComputed =
obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, prov);
CurrencyParameterSensitivities parameterSensitivityComputed =
prov.parameterSensitivity(sensitivityBuilderComputed.build());
CurrencyParameterSensitivities parameterSensitivityExpected =
CAL_FD.sensitivity(prov, (p) -> CurrencyAmount.of(USD_FED_FUND.getCurrency(),
obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, (p))));
assertTrue(parameterSensitivityComputed.equalWithTolerance(parameterSensitivityExpected, EPS_FD * 10.0));
}
}
/** Test parameter sensitivity with finite difference sensitivity calculator. No cutoff period. */
public void rateChfNoCutOffParameterSensitivity() {
LocalDate[] valuationDate = {date(2015, 1, 1), date(2015, 1, 8)};
DoubleArray time = DoubleArray.of(0.0, 0.5, 1.0, 2.0, 5.0, 10.0);
DoubleArray rate = DoubleArray.of(0.0100, 0.0110, 0.0115, 0.0130, 0.0135, 0.0135);
for (int loopvaldate = 0; loopvaldate < 2; loopvaldate++) {
Curve onCurve = InterpolatedNodalCurve.of(
Curves.zeroRates("ON", ACT_ACT_ISDA), time, rate, INTERPOLATOR);
ImmutableRatesProvider prov = ImmutableRatesProvider.builder(valuationDate[loopvaldate])
.overnightIndexCurve(CHF_TOIS, onCurve, TIME_SERIES)
.build();
OvernightAveragedRateComputation ro =
OvernightAveragedRateComputation.of(CHF_TOIS, START_DATE, END_DATE, 0, REF_DATA);
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
PointSensitivityBuilder sensitivityBuilderComputed =
obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, prov);
CurrencyParameterSensitivities parameterSensitivityComputed =
prov.parameterSensitivity(sensitivityBuilderComputed.build());
CurrencyParameterSensitivities parameterSensitivityExpected =
CAL_FD.sensitivity(prov, (p) -> CurrencyAmount.of(CHF_TOIS.getCurrency(),
obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, (p))));
assertTrue(parameterSensitivityComputed.equalWithTolerance(parameterSensitivityExpected, EPS_FD * 10.0));
}
}
private Double[] computedSensitivityFD(
OvernightAveragedRateComputation ro, OvernightIndex index, OvernightIndexObservation[] indexObs) {
int nRates = FIXING_DATES.length;
OvernightIndexRates[] mockRatesUp = new OvernightIndexRates[nRates];
SimpleRatesProvider[] simpleProvUp = new SimpleRatesProvider[nRates];
OvernightIndexRates[] mockRatesDw = new OvernightIndexRates[nRates];
SimpleRatesProvider[] simpleProvDw = new SimpleRatesProvider[nRates];
double[][] ratesUp = new double[nRates][];
double[][] ratesDw = new double[nRates][];
for (int i = 0; i < nRates; ++i) {
mockRatesUp[i] = mock(OvernightIndexRates.class);
simpleProvUp[i] = new SimpleRatesProvider(mockRatesUp[i]);
mockRatesDw[i] = mock(OvernightIndexRates.class);
simpleProvDw[i] = new SimpleRatesProvider(mockRatesDw[i]);
ratesUp[i] = Arrays.copyOf(FIXING_RATES, nRates);
ratesDw[i] = Arrays.copyOf(FIXING_RATES, nRates);
ratesUp[i][i] += EPS_FD;
ratesDw[i][i] -= EPS_FD;
}
for (int i = 0; i < nRates; i++) {
for (int j = 0; j < nRates; ++j) {
when(mockRatesUp[j].rate(indexObs[i])).thenReturn(ratesUp[j][i]);
when(mockRatesDw[j].rate(indexObs[i])).thenReturn(ratesDw[j][i]);
}
}
ForwardOvernightAveragedRateComputationFn obsFn = ForwardOvernightAveragedRateComputationFn.DEFAULT;
List<Double> sensitivityExpected = new ArrayList<Double>();
for (int i = 0; i < nRates; ++i) {
double rateUp = obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProvUp[i]);
double rateDw = obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProvDw[i]);
double res = 0.5 * (rateUp - rateDw) / EPS_FD;
if (Math.abs(res) > 1.0e-14) {
sensitivityExpected.add(res);
}
}
int size = sensitivityExpected.size();
Double[] result = new Double[size];
return sensitivityExpected.toArray(result);
}
}