/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.payments.provider;
import static org.testng.AssertJUnit.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.testng.annotations.Test;
import org.threeten.bp.Period;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.analytics.financial.instrument.index.IborIndex;
import com.opengamma.analytics.financial.instrument.payment.CouponIborAverageFixingDatesCompoundingDefinition;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIborAverageFixingDatesCompounding;
import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueCurveSensitivityDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingCalculator;
import com.opengamma.analytics.financial.provider.description.MulticurveProviderDiscountDataSets;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.ParameterSensitivityMulticurveDiscountInterpolatedFDCalculator;
import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterCalculator;
import com.opengamma.analytics.financial.schedule.ScheduleCalculator;
import com.opengamma.analytics.financial.util.AssertSensitivityObjects;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.financial.convention.calendar.MondayToFridayCalendar;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.financial.convention.daycount.DayCounts;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;
import com.opengamma.util.time.DateUtils;
/**
* Tests related to the CouponIborAverageCompoundingDiscountingMethod.
*/
public class CouponIborAverageFixingDatesCompoundingDiscountingMethodTest {
private static final MulticurveProviderDiscount MULTICURVES = MulticurveProviderDiscountDataSets.createMulticurveEurUsd();
private static final IborIndex[] IBOR_INDEXES = MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd();
private static final Calendar CALENDAR = new MondayToFridayCalendar("A");
private static final IborIndex INDEX1 = IBOR_INDEXES[0]; // EURIBOR3M
private static final IborIndex INDEX2 = IBOR_INDEXES[3]; // LIBOR6M
private static final Currency CUR1 = Currency.EUR;
private static final Currency CUR2 = Currency.USD;
private static final DayCount DAY_COUNT_INDEX = INDEX1.getDayCount();
private static final int NUM_PRDS = 6;
private static final int NUM_OBS = 5;
private static final int NUM_OBS_INI = 3;
private static final ZonedDateTime ACCRUAL_START_DATE = DateUtils.getUTCDate(2011, 1, 6);
private static final ZonedDateTime ACCRUAL_END_DATE = DateUtils.getUTCDate(2011, 7, 4);
private static final ZonedDateTime PAYMENT_DATE = DateUtils.getUTCDate(2011, 7, 6);
// The above dates are not standard but selected for insure correct testing.
private static final ZonedDateTime[][] FIXING_DATES = new ZonedDateTime[NUM_PRDS][NUM_OBS];
private static final double[][] WEIGHTS1 = new double[NUM_PRDS][NUM_OBS];
private static final double[][] WEIGHTS2 = new double[NUM_PRDS][NUM_OBS];
static {
for (int i = 0; i < NUM_OBS; ++i) {
for (int j = 0; j < NUM_PRDS; ++j) {
FIXING_DATES[j][i] = DateUtils.getUTCDate(2011, j + 1, 3 + 6 * i);
WEIGHTS1[j][i] = 2. * (NUM_OBS - i) / NUM_OBS / (NUM_OBS + 1.);
WEIGHTS2[j][i] = 2. * (NUM_OBS - i) / NUM_OBS / (NUM_OBS + 1.);
}
}
}
private static final DayCount DAY_COUNT_PAYMENT = DayCounts.ACT_365;
private static final double ACCRUAL_FACTOR = DAY_COUNT_PAYMENT.getDayCountFraction(ACCRUAL_START_DATE, ACCRUAL_END_DATE);
private static final double[] ACCRUAL_FACTORS = new double[NUM_PRDS];
static {
Arrays.fill(ACCRUAL_FACTORS, ACCRUAL_FACTOR / NUM_PRDS);
}
private static final double NOTIONAL = 100000000; // 100m
private static final ZonedDateTime REFERENCE_DATE1 = FIXING_DATES[0][1].plusDays(1);
private static final ZonedDateTime REFERENCE_DATE2 = DateUtils.getUTCDate(2010, 12, 27);
private static ZonedDateTime[][] EXP_START_DATES1 = new ZonedDateTime[NUM_PRDS][NUM_OBS];
private static ZonedDateTime[][] EXP_END_DATES1 = new ZonedDateTime[NUM_PRDS][NUM_OBS];
private static ZonedDateTime[][] EXP_START_DATES2 = new ZonedDateTime[NUM_PRDS][NUM_OBS];
private static ZonedDateTime[][] EXP_END_DATES2 = new ZonedDateTime[NUM_PRDS][NUM_OBS];
static {
for (int i = 0; i < NUM_OBS; ++i) {
for (int j = 0; j < NUM_PRDS; ++j) {
EXP_START_DATES1[j][i] = ScheduleCalculator.getAdjustedDate(FIXING_DATES[j][i], INDEX1.getSpotLag(), CALENDAR);
EXP_END_DATES1[j][i] = ScheduleCalculator.getAdjustedDate(EXP_START_DATES1[j][i], INDEX1.getTenor(),
INDEX1.getBusinessDayConvention(), CALENDAR, INDEX1.isEndOfMonth());
EXP_START_DATES2[j][i] = ScheduleCalculator.getAdjustedDate(FIXING_DATES[j][i], INDEX2.getSpotLag(), CALENDAR);
EXP_END_DATES2[j][i] = ScheduleCalculator.getAdjustedDate(EXP_START_DATES2[j][i], INDEX2.getTenor(),
INDEX2.getBusinessDayConvention(), CALENDAR, INDEX2.isEndOfMonth());
}
}
}
private static final double PAYMENT_TIME1 = TimeCalculator.getTimeBetween(REFERENCE_DATE1, PAYMENT_DATE);
private static final double PAYMENT_TIME2 = TimeCalculator.getTimeBetween(REFERENCE_DATE2, PAYMENT_DATE);
private static final double[][] FIXING_TIMES1 = new double[NUM_PRDS][NUM_OBS];
private static final double[][] FIXING_PERIOD_START_TIMES1 = new double[NUM_PRDS][NUM_OBS];
private static final double[][] FIXING_PERIOD_END_TIMES1 = new double[NUM_PRDS][NUM_OBS];
private static double[][] FIX_ACC_FACTORS1 = new double[NUM_PRDS][NUM_OBS];
private static final double[][] FIXING_TIMES2 = new double[NUM_PRDS][NUM_OBS];
private static final double[][] FIXING_PERIOD_START_TIMES2 = new double[NUM_PRDS][NUM_OBS];
private static final double[][] FIXING_PERIOD_END_TIMES2 = new double[NUM_PRDS][NUM_OBS];
private static double[][] FIX_ACC_FACTORS2 = new double[NUM_PRDS][NUM_OBS];
static {
for (int i = 0; i < NUM_PRDS; ++i) {
FIXING_TIMES1[i] = TimeCalculator.getTimeBetween(REFERENCE_DATE1, FIXING_DATES[i]);
FIXING_PERIOD_START_TIMES1[i] = TimeCalculator.getTimeBetween(REFERENCE_DATE1, EXP_START_DATES1[i]);
FIXING_PERIOD_END_TIMES1[i] = TimeCalculator.getTimeBetween(REFERENCE_DATE1, EXP_END_DATES2[i]);
FIXING_TIMES2[i] = TimeCalculator.getTimeBetween(REFERENCE_DATE2, FIXING_DATES[i]);
FIXING_PERIOD_START_TIMES2[i] = TimeCalculator.getTimeBetween(REFERENCE_DATE2, EXP_START_DATES2[i]);
FIXING_PERIOD_END_TIMES2[i] = TimeCalculator.getTimeBetween(REFERENCE_DATE2, EXP_END_DATES2[i]);
}
for (int i = 0; i < NUM_OBS; ++i) {
for (int j = 0; j < NUM_PRDS; ++j) {
FIX_ACC_FACTORS1[j][i] = INDEX1.getDayCount().getDayCountFraction(EXP_START_DATES1[j][i], EXP_END_DATES1[j][i], CALENDAR);
FIX_ACC_FACTORS2[j][i] = INDEX2.getDayCount().getDayCountFraction(EXP_START_DATES2[j][i], EXP_END_DATES2[j][i], CALENDAR);
}
}
}
private static final double[] FIXING_TIMES_INI = new double[NUM_OBS_INI];
private static final double[] FIXING_PERIOD_START_TIMES_INI = new double[NUM_OBS_INI];
private static final double[] FIXING_PERIOD_END_TIMES_INI = new double[NUM_OBS_INI];
private static double[] FIX_ACC_FACTORS_INI = new double[NUM_OBS_INI];
private static final double[] WEIGHTS_INI = new double[NUM_OBS_INI];
static {
for (int i = 0; i < NUM_OBS_INI; ++i) {
FIXING_TIMES_INI[i] = FIXING_TIMES1[0][NUM_OBS - NUM_OBS_INI + i];
FIXING_PERIOD_START_TIMES_INI[i] = FIXING_PERIOD_START_TIMES1[0][NUM_OBS - NUM_OBS_INI + i];
FIXING_PERIOD_END_TIMES_INI[i] = FIXING_PERIOD_END_TIMES1[0][NUM_OBS - NUM_OBS_INI + i];
FIX_ACC_FACTORS_INI[i] = FIX_ACC_FACTORS1[0][NUM_OBS - NUM_OBS_INI + i];
WEIGHTS_INI[i] = WEIGHTS1[0][NUM_OBS - NUM_OBS_INI + i];
}
FIXING_TIMES1[0] = FIXING_TIMES_INI;
FIXING_PERIOD_START_TIMES1[0] = FIXING_PERIOD_START_TIMES_INI;
FIXING_PERIOD_END_TIMES1[0] = FIXING_PERIOD_END_TIMES_INI;
FIX_ACC_FACTORS1[0] = FIX_ACC_FACTORS_INI;
WEIGHTS1[0] = WEIGHTS_INI;
}
private static final double AMOUNT_ACC = 0.005;
private static final double RATE_FIXED = 1.02;
//Examples 1 & 2: same numbers of fixing
private static final CouponIborAverageFixingDatesCompounding DER1 = new CouponIborAverageFixingDatesCompounding(CUR1,
PAYMENT_TIME1, ACCRUAL_FACTOR, NOTIONAL, ACCRUAL_FACTORS, INDEX1, FIXING_TIMES1, WEIGHTS1,
FIXING_PERIOD_START_TIMES1, FIXING_PERIOD_END_TIMES1, FIX_ACC_FACTORS1, AMOUNT_ACC, RATE_FIXED);
private static final CouponIborAverageFixingDatesCompounding DER2 = new CouponIborAverageFixingDatesCompounding(CUR2,
PAYMENT_TIME2, ACCRUAL_FACTOR, NOTIONAL, ACCRUAL_FACTORS, INDEX2, FIXING_TIMES2, WEIGHTS2,
FIXING_PERIOD_START_TIMES2, FIXING_PERIOD_END_TIMES2, FIX_ACC_FACTORS2, 0.0, 1.0);
// Example 3: different number of fixing in each subperiod
private static final int NB_SUBPERIODS = 3;
private static final ZonedDateTime[] ACCRUAL_START_DATE_SUB_3 = new ZonedDateTime[NB_SUBPERIODS + 1];
private static final ZonedDateTime[][] FIXING_DATES_3 = new ZonedDateTime[NB_SUBPERIODS][];
private static final double[][] WEIGHTS_3 = new double[NB_SUBPERIODS][];
private static final double[] ACCRUAL_FACTORS_3 = new double[NB_SUBPERIODS];
static {
for (int loopsub = 0; loopsub <= NB_SUBPERIODS; loopsub++) {
ACCRUAL_START_DATE_SUB_3[loopsub] = ScheduleCalculator.getAdjustedDate(ACCRUAL_START_DATE, Period.ofMonths(loopsub), INDEX1, CALENDAR);
}
for (int loopsub = 0; loopsub < NB_SUBPERIODS; loopsub++) {
List<ZonedDateTime> listDates = new ArrayList<>();
ZonedDateTime startFixPeriod = ACCRUAL_START_DATE_SUB_3[loopsub];
listDates.add(ScheduleCalculator.getAdjustedDate(startFixPeriod, -INDEX1.getSpotLag(), CALENDAR));
startFixPeriod = ScheduleCalculator.getAdjustedDate(startFixPeriod, 1, CALENDAR);
while (startFixPeriod.isBefore(ACCRUAL_START_DATE_SUB_3[loopsub + 1])) {
listDates.add(ScheduleCalculator.getAdjustedDate(startFixPeriod, -INDEX1.getSpotLag(), CALENDAR));
startFixPeriod = ScheduleCalculator.getAdjustedDate(startFixPeriod, 1, CALENDAR);
}
FIXING_DATES_3[loopsub] = listDates.toArray(new ZonedDateTime[0]);
WEIGHTS_3[loopsub] = new double[FIXING_DATES_3[loopsub].length];
ACCRUAL_FACTORS_3[loopsub] = DAY_COUNT_INDEX.getDayCountFraction(ACCRUAL_START_DATE_SUB_3[loopsub], ACCRUAL_START_DATE_SUB_3[loopsub + 1]);
for (int loopf = 0; loopf < FIXING_DATES_3[loopsub].length; loopf++) {
WEIGHTS_3[loopsub][loopf] = 1.0d / FIXING_DATES_3[loopsub].length;
}
}
}
private static final CouponIborAverageFixingDatesCompoundingDefinition DFN3 = new CouponIborAverageFixingDatesCompoundingDefinition(CUR1, ACCRUAL_START_DATE_SUB_3[3],
ACCRUAL_START_DATE_SUB_3[0], ACCRUAL_START_DATE_SUB_3[3], ACCRUAL_FACTOR, NOTIONAL, ACCRUAL_FACTORS_3, INDEX1, FIXING_DATES_3, WEIGHTS_3, CALENDAR);
private static final CouponIborAverageFixingDatesCompounding DER3 = DFN3.toDerivative(FIXING_DATES_3[0][0].minusDays(1));
private static final CouponIborAverageFixingDatesCompoundingDiscountingMethod METHOD = CouponIborAverageFixingDatesCompoundingDiscountingMethod.getInstance();
private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance();
private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC = PresentValueCurveSensitivityDiscountingCalculator.getInstance();
private static final ParameterSensitivityParameterCalculator<ParameterProviderInterface> PSC = new ParameterSensitivityParameterCalculator<>(PVCSDC);
private static final double SHIFT = 1.0E-7;
private static final ParameterSensitivityMulticurveDiscountInterpolatedFDCalculator PSC_DSC_FD = new ParameterSensitivityMulticurveDiscountInterpolatedFDCalculator(PVDC, SHIFT);
private static final double EPS = 1.0e-10;
private static final double TOLERANCE_PV = 1.0E-2;
private static final double TOLERANCE_PV_DELTA = 1.0E2;
/**
* pv for partially fixed coupon
*/
@Test
public void presentValueFixedTest() {
final MultipleCurrencyAmount pvComputed = METHOD.presentValue(DER1, MULTICURVES);
double fwd = AMOUNT_ACC;
for (int j = 0; j < NUM_OBS_INI; ++j) {
fwd += WEIGHTS1[0][j] * MULTICURVES.getSimplyCompoundForwardRate(INDEX1, FIXING_PERIOD_START_TIMES1[0][j], FIXING_PERIOD_END_TIMES1[0][j], FIX_ACC_FACTORS1[0][j]);
}
double invFactor = RATE_FIXED;
invFactor *= (1.0 + fwd * DER1.getPaymentAccrualFactors()[0]);
for (int i = 1; i < NUM_PRDS; ++i) {
double forward = 0.0;
for (int j = 0; j < NUM_OBS; ++j) {
forward += WEIGHTS1[i][j] * MULTICURVES.getSimplyCompoundForwardRate(INDEX1, FIXING_PERIOD_START_TIMES1[i][j], FIXING_PERIOD_END_TIMES1[i][j], FIX_ACC_FACTORS1[i][j]);
}
invFactor *= (1.0 + forward * DER1.getPaymentAccrualFactors()[i]);
}
final double pvExpected = DER1.getNotional() * MULTICURVES.getDiscountFactor(DER1.getCurrency(), DER1.getPaymentTime()) * (invFactor - 1.0);
assertEquals("CouponIborAverageCompoundingDiscountingMethod: present value", pvExpected, pvComputed.getAmount(DER1.getCurrency()), TOLERANCE_PV);
final MultipleCurrencyAmount pvWithCalc = PVDC.visitCouponIborAverageCompounding(DER1, MULTICURVES);
assertEquals("CouponIborAverageCompoundingDiscountingMethod: present value", pvWithCalc.getAmount(DER1.getCurrency()),
pvComputed.getAmount(DER1.getCurrency()), TOLERANCE_PV);
}
/**
* pv for no fixed coupon
*/
@Test
public void presentValueNotFixedTest() {
final MultipleCurrencyAmount pvComputed = METHOD.presentValue(DER2, MULTICURVES);
double invFactor = 1.0;
for (int i = 0; i < NUM_PRDS; ++i) {
double forward = 0.0;
for (int j = 0; j < NUM_OBS; ++j) {
forward += WEIGHTS2[i][j] * MULTICURVES.getSimplyCompoundForwardRate(INDEX2, FIXING_PERIOD_START_TIMES2[i][j], FIXING_PERIOD_END_TIMES2[i][j], FIX_ACC_FACTORS2[i][j]);
}
invFactor *= (1.0 + forward * DER2.getPaymentAccrualFactors()[i]);
}
final double pvExpected = DER2.getNotional() * MULTICURVES.getDiscountFactor(DER2.getCurrency(), DER2.getPaymentTime()) * (invFactor - 1.0);
assertEquals(pvExpected, pvComputed.getAmount(DER2.getCurrency()), TOLERANCE_PV);
final MultipleCurrencyAmount pvWithCalc = PVDC.visitCouponIborAverageCompounding(DER2, MULTICURVES);
assertEquals(pvWithCalc.getAmount(DER2.getCurrency()), pvComputed.getAmount(DER2.getCurrency()), EPS * Math.abs(pvExpected));
}
/**
* pv for no fixed coupon with different numbers of fixing dates
*/
@Test
public void presentValueNotFixedNoSquareTest() {
final MultipleCurrencyAmount pvComputed = METHOD.presentValue(DER3, MULTICURVES);
double invFactor = DER3.getInvestmentFactor();
double fwd = DER3.getAmountAccrued();
for (int j = 0; j < DER3.getFixingPeriodAccrualFactor()[0].length; ++j) {
fwd += WEIGHTS_3[0][j] * MULTICURVES.getSimplyCompoundForwardRate(INDEX1, DER3.getFixingPeriodStartTime()[0][j],
DER3.getFixingPeriodEndTime()[0][j], DER3.getFixingPeriodAccrualFactor()[0][j]);
}
invFactor *= (1.0 + fwd * DER3.getPaymentAccrualFactors()[0]);
for (int i = 1; i < DER3.getFixingPeriodAccrualFactor().length; ++i) {
double forward = 0.0;
for (int j = 0; j < DER3.getFixingPeriodAccrualFactor()[i].length; ++j) {
forward += WEIGHTS_3[i][j] *
MULTICURVES.getSimplyCompoundForwardRate(INDEX1, DER3.getFixingPeriodStartTime()[i][j], DER3.getFixingPeriodEndTime()[i][j], DER3.getFixingPeriodAccrualFactor()[i][j]);
}
invFactor *= (1.0 + forward * DER3.getPaymentAccrualFactors()[i]);
}
final double pvExpected = DER3.getNotional() * MULTICURVES.getDiscountFactor(DER3.getCurrency(), DER3.getPaymentTime()) * (invFactor - 1.0);
assertEquals(pvExpected, pvComputed.getAmount(DER3.getCurrency()), EPS * Math.abs(pvExpected));
final MultipleCurrencyAmount pvWithCalc = PVDC.visitCouponIborAverageCompounding(DER3, MULTICURVES);
assertEquals(pvWithCalc.getAmount(DER3.getCurrency()), pvComputed.getAmount(DER3.getCurrency()), EPS * Math.abs(pvExpected));
}
@Test
public void presentValueCurveSensitivityFiniteDifference() {
final MultipleCurrencyParameterSensitivity senseCalc1 = PSC.calculateSensitivity(DER1, MULTICURVES, MULTICURVES.getAllNames());
final MultipleCurrencyParameterSensitivity senseFd1 = PSC_DSC_FD.calculateSensitivity(DER1, MULTICURVES);
AssertSensitivityObjects.assertEquals("CouponIborAverageCompoundingDiscountingMethod", senseCalc1, senseFd1, TOLERANCE_PV_DELTA);
final MultipleCurrencyParameterSensitivity senseCalc2 = PSC.calculateSensitivity(DER2, MULTICURVES, MULTICURVES.getAllNames());
final MultipleCurrencyParameterSensitivity senseFd2 = PSC_DSC_FD.calculateSensitivity(DER2, MULTICURVES);
AssertSensitivityObjects.assertEquals("CouponIborAverageCompoundingDiscountingMethod", senseCalc2, senseFd2, TOLERANCE_PV_DELTA);
}
@Test
public void presentValueCurveSensitivityMethodVsCalculator() {
final MultipleCurrencyMulticurveSensitivity pvcsMethod1 = METHOD.presentValueCurveSensitivity(DER1, MULTICURVES);
final MultipleCurrencyMulticurveSensitivity pvcsCalculator1 = DER1.accept(PVCSDC, MULTICURVES);
AssertSensitivityObjects.assertEquals("CouponIborAverageCompoundingDiscountingMethod", pvcsMethod1, pvcsCalculator1, EPS);
final MultipleCurrencyMulticurveSensitivity pvcsMethod2 = METHOD.presentValueCurveSensitivity(DER2, MULTICURVES);
final MultipleCurrencyMulticurveSensitivity pvcsCalculator2 = DER2.accept(PVCSDC, MULTICURVES);
AssertSensitivityObjects.assertEquals("CouponIborAverageCompoundingDiscountingMethod", pvcsMethod2, pvcsCalculator2, EPS);
}
}