/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.capfloor;
import static com.opengamma.strata.basics.date.DayCounts.ACT_ACT_ISDA;
import static com.opengamma.strata.basics.index.IborIndices.USD_LIBOR_3M;
import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg;
import static com.opengamma.strata.market.curve.interpolator.CurveInterpolators.DOUBLE_QUADRATIC;
import static com.opengamma.strata.market.curve.interpolator.CurveInterpolators.LINEAR;
import static org.testng.Assert.assertEquals;
import java.time.Period;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.collect.tuple.Pair;
import com.opengamma.strata.market.ValueType;
import com.opengamma.strata.market.curve.ConstantCurve;
import com.opengamma.strata.market.surface.ConstantSurface;
import com.opengamma.strata.market.surface.InterpolatedNodalSurface;
import com.opengamma.strata.market.surface.Surfaces;
import com.opengamma.strata.pricer.common.GenericVolatilitySurfacePeriodParameterMetadata;
import com.opengamma.strata.pricer.option.RawOptionData;
import com.opengamma.strata.product.capfloor.ResolvedIborCapFloorLeg;
/**
* Test {@link SurfaceIborCapletFloorletVolatilityBootstrapper}.
*/
@Test
public class SurfaceIborCapletFloorletVolatilityBootstrapperTest extends CapletStrippingSetup {
private static final SurfaceIborCapletFloorletVolatilityBootstrapper CALIBRATOR =
SurfaceIborCapletFloorletVolatilityBootstrapper.DEFAULT;
private static final BlackIborCapFloorLegPricer LEG_PRICER_BLACK = BlackIborCapFloorLegPricer.DEFAULT;
private static final NormalIborCapFloorLegPricer LEG_PRICER_NORMAL = NormalIborCapFloorLegPricer.DEFAULT;
private static final double TOL = 1.0e-14;
public void recovery_test_blackSurface() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, LINEAR);
DoubleArray strikes = createBlackStrikes();
RawOptionData data = RawOptionData.of(
createBlackMaturities(), strikes, ValueType.STRIKE, createFullBlackDataMatrix(), ValueType.BLACK_VOLATILITY);
IborCapletFloorletVolatilityCalibrationResult res = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
BlackIborCapletFloorletExpiryStrikeVolatilities resVol =
(BlackIborCapletFloorletExpiryStrikeVolatilities) res.getVolatilities();
for (int i = 0; i < strikes.size(); ++i) {
Pair<List<ResolvedIborCapFloorLeg>, List<Double>> capsAndVols = getCapsBlackVols(i);
List<ResolvedIborCapFloorLeg> caps = capsAndVols.getFirst();
List<Double> vols = capsAndVols.getSecond();
int nCaps = caps.size();
for (int j = 0; j < nCaps; ++j) {
ConstantSurface volSurface = ConstantSurface.of(
Surfaces.blackVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols.get(j));
BlackIborCapletFloorletExpiryStrikeVolatilities constVol = BlackIborCapletFloorletExpiryStrikeVolatilities.of(
USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
double priceOrg = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, constVol).getAmount();
double priceCalib = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, resVol).getAmount();
assertEquals(priceOrg, priceCalib, Math.max(priceOrg, 1d) * TOL);
}
}
assertEquals(res.getChiSquare(), 0d);
assertEquals(resVol.getIndex(), USD_LIBOR_3M);
assertEquals(resVol.getName(), definition.getName());
assertEquals(resVol.getValuationDateTime(), CALIBRATION_TIME);
InterpolatedNodalSurface surface = (InterpolatedNodalSurface) resVol.getSurface();
for (int i = 0; i < surface.getParameterCount(); ++i) {
GenericVolatilitySurfacePeriodParameterMetadata metadata =
(GenericVolatilitySurfacePeriodParameterMetadata) surface.getParameterMetadata(i);
assertEquals(metadata.getStrike().getValue(), surface.getYValues().get(i));
}
}
public void test_invalid_data() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, LINEAR);
DoubleArray strikes = createBlackStrikes();
RawOptionData data = RawOptionData.of(
createBlackMaturities(), strikes, ValueType.STRIKE, createFullBlackDataMatrixInvalid(), ValueType.BLACK_VOLATILITY);
assertThrowsIllegalArg(() -> CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER));
}
public void recovery_test_blackSurface_shift() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, LINEAR,
ConstantCurve.of("Black shift", 0.02));
DoubleArray strikes = createBlackStrikes();
RawOptionData data = RawOptionData.of(
createBlackMaturities(), strikes, ValueType.STRIKE, createFullBlackDataMatrix(), ValueType.BLACK_VOLATILITY);
IborCapletFloorletVolatilityCalibrationResult res = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities resVol =
(ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities) res.getVolatilities();
for (int i = 0; i < strikes.size(); ++i) {
Pair<List<ResolvedIborCapFloorLeg>, List<Double>> capsAndVols = getCapsBlackVols(i);
List<ResolvedIborCapFloorLeg> caps = capsAndVols.getFirst();
List<Double> vols = capsAndVols.getSecond();
int nCaps = caps.size();
for (int j = 0; j < nCaps; ++j) {
ConstantSurface volSurface = ConstantSurface.of(
Surfaces.blackVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols.get(j));
BlackIborCapletFloorletExpiryStrikeVolatilities constVol = BlackIborCapletFloorletExpiryStrikeVolatilities.of(
USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
double priceOrg = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, constVol).getAmount();
double priceCalib = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, resVol).getAmount();
assertEquals(priceOrg, priceCalib, Math.max(priceOrg, 1d) * TOL);
}
}
assertEquals(res.getChiSquare(), 0d);
assertEquals(resVol.getIndex(), USD_LIBOR_3M);
assertEquals(resVol.getName(), definition.getName());
assertEquals(resVol.getValuationDateTime(), CALIBRATION_TIME);
assertEquals(resVol.getShiftCurve(), definition.getShiftCurve().get());
InterpolatedNodalSurface surface = (InterpolatedNodalSurface) resVol.getSurface();
for (int i = 0; i < surface.getParameterCount(); ++i) {
GenericVolatilitySurfacePeriodParameterMetadata metadata =
(GenericVolatilitySurfacePeriodParameterMetadata) surface.getParameterMetadata(i);
assertEquals(metadata.getStrike().getValue() + 0.02, surface.getYValues().get(i));
}
}
public void recovery_test_blackCurve() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, LINEAR);
DoubleArray strikes = createBlackStrikes();
for (int i = 0; i < strikes.size(); ++i) {
Pair<List<Period>, DoubleMatrix> trimedData = trimData(createBlackMaturities(), createBlackDataMatrixForStrike(i));
RawOptionData data = RawOptionData.of(
trimedData.getFirst(), DoubleArray.of(strikes.get(i)), ValueType.STRIKE, trimedData.getSecond(),
ValueType.BLACK_VOLATILITY);
IborCapletFloorletVolatilityCalibrationResult res =
CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
BlackIborCapletFloorletExpiryStrikeVolatilities resVol =
(BlackIborCapletFloorletExpiryStrikeVolatilities) res.getVolatilities();
Pair<List<ResolvedIborCapFloorLeg>, List<Double>> capsAndVols = getCapsBlackVols(i);
List<ResolvedIborCapFloorLeg> caps = capsAndVols.getFirst();
List<Double> vols = capsAndVols.getSecond();
int nCaps = caps.size();
for (int j = 0; j < nCaps; ++j) {
ConstantSurface volSurface = ConstantSurface.of(
Surfaces.blackVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols.get(j));
BlackIborCapletFloorletExpiryStrikeVolatilities constVol = BlackIborCapletFloorletExpiryStrikeVolatilities.of(
USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
double priceOrg = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, constVol).getAmount();
double priceCalib = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, resVol).getAmount();
assertEquals(priceOrg, priceCalib, Math.max(priceOrg, 1d) * TOL);
}
}
}
public void recovery_test_flat() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, LINEAR);
DoubleArray strikes = createBlackStrikes();
RawOptionData data = RawOptionData.of(
createBlackMaturities(), strikes, ValueType.STRIKE, createFullFlatBlackDataMatrix(), ValueType.BLACK_VOLATILITY);
IborCapletFloorletVolatilityCalibrationResult res = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
BlackIborCapletFloorletExpiryStrikeVolatilities resVol =
(BlackIborCapletFloorletExpiryStrikeVolatilities) res.getVolatilities();
for (int i = 0; i < NUM_BLACK_STRIKES; ++i) {
Pair<List<ResolvedIborCapFloorLeg>, List<Double>> capsAndVols = getCapsFlatBlackVols(i);
List<ResolvedIborCapFloorLeg> caps = capsAndVols.getFirst();
List<Double> vols = capsAndVols.getSecond();
int nCaps = caps.size();
for (int j = 0; j < nCaps; ++j) {
ConstantSurface volSurface = ConstantSurface.of(
Surfaces.blackVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols.get(j));
BlackIborCapletFloorletExpiryStrikeVolatilities constVol = BlackIborCapletFloorletExpiryStrikeVolatilities.of(
USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
double priceOrg = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, constVol).getAmount();
double priceCalib = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, resVol).getAmount();
assertEquals(priceOrg, priceCalib, Math.max(priceOrg, 1d) * TOL);
}
}
assertEquals(res.getChiSquare(), 0d);
}
public void recovery_test_normal1() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, DOUBLE_QUADRATIC);
DoubleArray strikes = createNormalStrikes();
RawOptionData data = RawOptionData.of(
createNormalMaturities(), strikes, ValueType.STRIKE, createFullNormalDataMatrix(), ValueType.NORMAL_VOLATILITY);
IborCapletFloorletVolatilityCalibrationResult res = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
NormalIborCapletFloorletExpiryStrikeVolatilities resVol =
(NormalIborCapletFloorletExpiryStrikeVolatilities) res.getVolatilities();
for (int i = 0; i < strikes.size(); ++i) {
Pair<List<ResolvedIborCapFloorLeg>, List<Double>> capsAndVols = getCapsNormalVols(i);
List<ResolvedIborCapFloorLeg> caps = capsAndVols.getFirst();
List<Double> vols = capsAndVols.getSecond();
int nCaps = caps.size();
for (int j = 0; j < nCaps; ++j) {
ConstantSurface volSurface = ConstantSurface.of(
Surfaces.normalVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols.get(j));
NormalIborCapletFloorletExpiryStrikeVolatilities constVol = NormalIborCapletFloorletExpiryStrikeVolatilities.of(
USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
double priceOrg = LEG_PRICER_NORMAL.presentValue(caps.get(j), RATES_PROVIDER, constVol).getAmount();
double priceCalib = LEG_PRICER_NORMAL.presentValue(caps.get(j), RATES_PROVIDER, resVol).getAmount();
assertEquals(priceOrg, priceCalib, Math.max(priceOrg, 1d) * TOL);
}
}
assertEquals(res.getChiSquare(), 0d);
assertEquals(res.getChiSquare(), 0d);
assertEquals(resVol.getIndex(), USD_LIBOR_3M);
assertEquals(resVol.getName(), definition.getName());
assertEquals(resVol.getValuationDateTime(), CALIBRATION_TIME);
InterpolatedNodalSurface surface = (InterpolatedNodalSurface) resVol.getSurface();
for (int i = 0; i < surface.getParameterCount(); ++i) {
GenericVolatilitySurfacePeriodParameterMetadata metadata =
(GenericVolatilitySurfacePeriodParameterMetadata) surface.getParameterMetadata(i);
assertEquals(metadata.getStrike().getValue(), surface.getYValues().get(i));
}
}
public void recovery_test_normal2() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, DOUBLE_QUADRATIC);
DoubleArray strikes = createNormalEquivStrikes();
RawOptionData data = RawOptionData.of(
createNormalEquivMaturities(), strikes, ValueType.STRIKE, createFullNormalEquivDataMatrix(), ValueType.NORMAL_VOLATILITY);
IborCapletFloorletVolatilityCalibrationResult res = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
NormalIborCapletFloorletExpiryStrikeVolatilities resVol =
(NormalIborCapletFloorletExpiryStrikeVolatilities) res.getVolatilities();
for (int i = 0; i < strikes.size(); ++i) {
Pair<List<ResolvedIborCapFloorLeg>, List<Double>> capsAndVols = getCapsNormalEquivVols(i);
List<ResolvedIborCapFloorLeg> caps = capsAndVols.getFirst();
List<Double> vols = capsAndVols.getSecond();
int nCaps = caps.size();
for (int j = 0; j < nCaps; ++j) {
ConstantSurface volSurface = ConstantSurface.of(
Surfaces.normalVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols.get(j));
NormalIborCapletFloorletExpiryStrikeVolatilities constVol = NormalIborCapletFloorletExpiryStrikeVolatilities.of(
USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
double priceOrg = LEG_PRICER_NORMAL.presentValue(caps.get(j), RATES_PROVIDER, constVol).getAmount();
double priceCalib = LEG_PRICER_NORMAL.presentValue(caps.get(j), RATES_PROVIDER, resVol).getAmount();
assertEquals(priceOrg, priceCalib, Math.max(priceOrg, 1d) * TOL);
}
}
assertEquals(res.getChiSquare(), 0d);
}
public void recovery_test_normal2_shift() {
SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(
IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, DOUBLE_QUADRATIC,
ConstantCurve.of("Black shift", 0.02));
DoubleArray strikes = createNormalEquivStrikes();
RawOptionData data = RawOptionData.of(
createNormalEquivMaturities(), strikes, ValueType.STRIKE, createFullNormalEquivDataMatrix(), ValueType.NORMAL_VOLATILITY);
IborCapletFloorletVolatilityCalibrationResult res = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities resVol =
(ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities) res.getVolatilities();
for (int i = 0; i < strikes.size(); ++i) {
Pair<List<ResolvedIborCapFloorLeg>, List<Double>> capsAndVols = getCapsNormalEquivVols(i);
List<ResolvedIborCapFloorLeg> caps = capsAndVols.getFirst();
List<Double> vols = capsAndVols.getSecond();
int nCaps = caps.size();
for (int j = 0; j < nCaps; ++j) {
ConstantSurface volSurface = ConstantSurface.of(
Surfaces.normalVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols.get(j));
NormalIborCapletFloorletExpiryStrikeVolatilities constVol = NormalIborCapletFloorletExpiryStrikeVolatilities.of(
USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
double priceOrg = LEG_PRICER_NORMAL.presentValue(caps.get(j), RATES_PROVIDER, constVol).getAmount();
double priceCalib = LEG_PRICER_BLACK.presentValue(caps.get(j), RATES_PROVIDER, resVol).getAmount();
assertEquals(priceOrg, priceCalib, Math.max(priceOrg, 1d) * TOL);
}
}
assertEquals(res.getChiSquare(), 0d);
}
//-------------------------------------------------------------------------
// remove null for one-dimensional bootstrapping
private Pair<List<Period>, DoubleMatrix> trimData(List<Period> expiries, DoubleMatrix vols) {
List<Period> resExpiries = new ArrayList<>();
List<Double> resVols = new ArrayList<>();
int size = vols.size();
for (int i = 0; i < size; ++i) {
if (Double.isFinite(vols.get(i, 0))) {
resExpiries.add(expiries.get(i));
resVols.add(vols.get(i, 0));
}
}
return Pair.of(resExpiries, DoubleMatrix.of(resExpiries.size(), 1, DoubleArray.copyOf(resVols).toArray()));
}
}