/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.market.curve;
import static com.opengamma.strata.collect.TestHelper.assertSerialization;
import static com.opengamma.strata.collect.TestHelper.coverBeanEquals;
import static com.opengamma.strata.collect.TestHelper.coverImmutableBean;
import static java.time.temporal.ChronoUnit.MONTHS;
import static org.testng.Assert.assertEquals;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.function.DoubleBinaryOperator;
import org.testng.annotations.Test;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.market.ShiftType;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolator;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolators;
import com.opengamma.strata.market.param.ParameterMetadata;
import com.opengamma.strata.market.param.UnitParameterSensitivity;
/**
* Test {@link InflationNodalCurve}.
*/
@Test
public class InflationNodalCurveTest {
private static final LocalDate VAL_DATE_1 = LocalDate.of(2016, 1, 1);
private static final LocalDate VAL_DATE_2 = LocalDate.of(2016, 10, 21);
private static final DoubleArray TIMES = DoubleArray.of(9.0, 21.0, 57.0, 117.0);
private static final DoubleArray VALUES = DoubleArray.of(240.500, 245.000, 265.000, 286.000);
private static final DoubleArray VALUES2 = DoubleArray.of(243.500, 248.000, 268.000, 289.000);
private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR;
private static final CurveName NAME = CurveName.of("USD-HICP");
private static final CurveMetadata METADATA = Curves.prices(NAME);
private static final InterpolatedNodalCurve CURVE_NOFIX = InterpolatedNodalCurve.of(METADATA, TIMES, VALUES, INTERPOLATOR);
private static final InterpolatedNodalCurve CURVE2_NOFIX = InterpolatedNodalCurve.of(METADATA, TIMES, VALUES2, INTERPOLATOR);
private static final YearMonth LAST_FIX_MONTH_1 = YearMonth.of(2015, 11);
private static final YearMonth LAST_FIX_MONTH_2 = YearMonth.of(2016, 7);
private static final double LAST_FIX_VALUE = 240.00;
private static final DoubleArray SEASONALITY_MULTIPLICATIVE = DoubleArray.of(
1.002754153722096, 1.001058905136103, 1.006398754528882, 1.000862459308375,
0.998885402944655, 0.995571243121412, 1.001419845026233, 1.001663068058397,
0.999147014890734, 0.998377467899150, 0.999570726482709, 0.994346721844999);
private static final SeasonalityDefinition SEASONALITY_MULTIPLICATIVE_DEF =
SeasonalityDefinition.of(SEASONALITY_MULTIPLICATIVE, ShiftType.SCALED);
private static final DoubleArray SEASONALITY_ADDITIVE = DoubleArray.of(
1.0, 1.5, 1.0, -0.5,
-0.5, -1.0, -1.5, 0.0,
0.5, 1.0, 1.0, -2.5);
private static final SeasonalityDefinition SEASONALITY_ADDITIVE_DEF =
SeasonalityDefinition.of(SEASONALITY_ADDITIVE, ShiftType.ABSOLUTE);
private static final double NB_MONTHS_1 = YearMonth.from(VAL_DATE_1).until(LAST_FIX_MONTH_1, MONTHS);
private static final double NB_MONTHS_2 = YearMonth.from(VAL_DATE_2).until(LAST_FIX_MONTH_2, MONTHS);
private static final NodalCurve EXTENDED_CURVE_2 = CURVE_NOFIX.withNode(NB_MONTHS_2, LAST_FIX_VALUE, ParameterMetadata.empty());
private static final DoubleArray SEASONALITY_MULTIPLICATIVE_COMP_2 =
seasonalityCompounded(VAL_DATE_2, LAST_FIX_MONTH_2, SEASONALITY_MULTIPLICATIVE, (v, a) -> v * a);
private static final YearMonth[] TEST_MONTHS = new YearMonth[] {
YearMonth.of(2016, 7), YearMonth.of(2016, 8), YearMonth.of(2016, 9), YearMonth.of(2016, 10),
YearMonth.of(2017, 1), YearMonth.of(2017, 6), YearMonth.of(2017, 7), YearMonth.of(2017, 8),
YearMonth.of(2018, 9), YearMonth.of(2025, 12)};
private static final double TOLERANCE_TIME = 1.0E-10;
private static final double TOLERANCE_VALUE = 1.0E-10;
private static final double TOLERANCE_DELTA = 1.0E-8;
public void of_construction_multiplicative_1() {
InflationNodalCurve curveComputed = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_1, LAST_FIX_MONTH_1, LAST_FIX_VALUE,
SEASONALITY_MULTIPLICATIVE_DEF);
assertEquals(curveComputed.getUnderlying().getXValues().get(0), NB_MONTHS_1, TOLERANCE_TIME);
assertEquals(curveComputed.getUnderlying().getYValues().get(0), LAST_FIX_VALUE, TOLERANCE_TIME);
assertEquals(curveComputed.yValue(NB_MONTHS_1), LAST_FIX_VALUE, TOLERANCE_TIME);
}
public void of_construction_multiplicative_2() {
InflationNodalCurve curveComputed = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE,
SEASONALITY_MULTIPLICATIVE_DEF);
assertEquals(curveComputed.getUnderlying().getXValues().get(0), NB_MONTHS_2, TOLERANCE_TIME);
assertEquals(curveComputed.getUnderlying().getYValues().get(0), LAST_FIX_VALUE, TOLERANCE_TIME);
assertEquals(curveComputed.yValue(NB_MONTHS_2), LAST_FIX_VALUE, TOLERANCE_TIME);
}
public void of_construction_additive_1() {
InflationNodalCurve curveComputed = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_1, LAST_FIX_MONTH_1, LAST_FIX_VALUE,
SEASONALITY_ADDITIVE_DEF);
assertEquals(curveComputed.getUnderlying().getXValues().get(0), NB_MONTHS_1, TOLERANCE_TIME);
assertEquals(curveComputed.getUnderlying().getYValues().get(0), LAST_FIX_VALUE, TOLERANCE_TIME);
assertEquals(curveComputed.yValue(NB_MONTHS_1), LAST_FIX_VALUE, TOLERANCE_TIME);
}
public void value_multiplicative() {
InflationNodalCurve curveComputed = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE,
SEASONALITY_MULTIPLICATIVE_DEF);
for (int i = 1; i < TEST_MONTHS.length; i++) {
double nbMonths = YearMonth.from(VAL_DATE_2).until(TEST_MONTHS[i], MONTHS);
double valueComputed = curveComputed.yValue(nbMonths);
int x = (int) ((nbMonths + 12) % 12);
double valueNoAdj = EXTENDED_CURVE_2.yValue(nbMonths);
double adj = SEASONALITY_MULTIPLICATIVE_COMP_2.get(x);
double valueExpected = valueNoAdj * adj;
assertEquals(valueExpected, valueComputed, TOLERANCE_VALUE);
}
}
public void value_additive() {
InflationNodalCurve curveComputed = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE,
SEASONALITY_ADDITIVE_DEF);
for (int i = 1; i < TEST_MONTHS.length; i++) {
double nbMonths = YearMonth.from(VAL_DATE_2).until(TEST_MONTHS[i], MONTHS);
double valueComputed = curveComputed.yValue(nbMonths);
int x = (int) ((nbMonths + 12) % 12);
double valueNoAdj = EXTENDED_CURVE_2.yValue(nbMonths);
DoubleArray seasonalityAdditiveCompounded =
seasonalityCompounded(VAL_DATE_2, LAST_FIX_MONTH_2, SEASONALITY_ADDITIVE, (v, a) -> v + a);
double adj = seasonalityAdditiveCompounded.get(x);
double valueExpected = valueNoAdj + adj;
assertEquals(valueExpected, valueComputed, TOLERANCE_VALUE);
}
}
public void parameter_sensitivity_multiplicative() {
InflationNodalCurve curve = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE,
SEASONALITY_MULTIPLICATIVE_DEF);
double shift = 1.0E-2;
for (int i = 1; i < TEST_MONTHS.length; i++) {
double nbMonths = YearMonth.from(VAL_DATE_2).until(TEST_MONTHS[i], MONTHS);
UnitParameterSensitivity psComputed = curve.yValueParameterSensitivity(nbMonths);
for (int j = 0; j < TIMES.size(); j++) {
double[] valuePM = new double[2];
for (int pm = 0; pm < 2; pm++) {
DoubleArray shiftedValues = VALUES.with(j, VALUES.get(j) + (1 - 2 * pm) * shift);
InterpolatedNodalCurve intCurveShifted = InterpolatedNodalCurve.of(METADATA, TIMES, shiftedValues, INTERPOLATOR);
InflationNodalCurve seaCurveShifted =
InflationNodalCurve.of(intCurveShifted, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE,
SEASONALITY_MULTIPLICATIVE_DEF);
valuePM[pm] = seaCurveShifted.yValue(nbMonths);
}
assertEquals(psComputed.getSensitivity().get(j), (valuePM[0] - valuePM[1]) / (2 * shift), TOLERANCE_DELTA);
}
}
}
public void parameter_sensitivity_additive() {
InflationNodalCurve curve = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE,
SEASONALITY_ADDITIVE_DEF);
double shift = 1.0E-2;
for (int i = 1; i < TEST_MONTHS.length; i++) {
double nbMonths = YearMonth.from(VAL_DATE_2).until(TEST_MONTHS[i], MONTHS);
UnitParameterSensitivity psComputed = curve.yValueParameterSensitivity(nbMonths);
for (int j = 0; j < TIMES.size(); j++) {
double[] valuePM = new double[2];
for (int pm = 0; pm < 2; pm++) {
DoubleArray shiftedValues = VALUES.with(j, VALUES.get(j) + (1 - 2 * pm) * shift);
InterpolatedNodalCurve intCurveShifted = InterpolatedNodalCurve.of(METADATA, TIMES, shiftedValues, INTERPOLATOR);
InflationNodalCurve seaCurveShifted =
InflationNodalCurve.of(intCurveShifted, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE,
SEASONALITY_ADDITIVE_DEF);
valuePM[pm] = seaCurveShifted.yValue(nbMonths);
}
assertEquals(psComputed.getSensitivity().get(j), (valuePM[0] - valuePM[1]) / (2 * shift), TOLERANCE_DELTA);
}
}
}
private static DoubleArray seasonalityCompounded(
LocalDate valDate, YearMonth fixingMonth, DoubleArray seasonality,
DoubleBinaryOperator adjustmentFunction) {
double nbMonths = YearMonth.from(valDate).until(fixingMonth, MONTHS);
double[] seasonalityCompoundedArray = new double[12];
int lastMonthIndex = fixingMonth.getMonth().getValue() - 2;
seasonalityCompoundedArray[(int) ((nbMonths + 12 + 1) % 12)] =
seasonality.get((lastMonthIndex + 1) % 12);
for (int i = 1; i < 12; i++) {
int j = (int) ((nbMonths + 12 + 1 + i) % 12);
seasonalityCompoundedArray[j] = adjustmentFunction.applyAsDouble(
seasonalityCompoundedArray[(j - 1 + 12) % 12], seasonality.get((lastMonthIndex + 1 + i) % 12));
}
return DoubleArray.ofUnsafe(seasonalityCompoundedArray);
}
//-------------------------------------------------------------------------
public void coverage() {
InflationNodalCurve test = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_1, LAST_FIX_MONTH_1, LAST_FIX_VALUE,
SEASONALITY_MULTIPLICATIVE_DEF);
coverImmutableBean(test);
InflationNodalCurve test2 = InflationNodalCurve
.of(CURVE2_NOFIX, VAL_DATE_2, LAST_FIX_MONTH_2, LAST_FIX_VALUE + 1.0d,
SEASONALITY_ADDITIVE_DEF);
coverBeanEquals(test, test2);
}
public void test_serialization() {
InflationNodalCurve test = InflationNodalCurve.of(CURVE_NOFIX, VAL_DATE_1, LAST_FIX_MONTH_1, LAST_FIX_VALUE,
SEASONALITY_MULTIPLICATIVE_DEF);
assertSerialization(test);
}
}