/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.payments.method;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
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.CapFloorIborDefinition;
import com.opengamma.analytics.financial.instrument.payment.CouponFixedDefinition;
import com.opengamma.analytics.financial.instrument.payment.CouponIborDefinition;
import com.opengamma.analytics.financial.interestrate.InterestRateCurveSensitivity;
import com.opengamma.analytics.financial.interestrate.ParRateCalculator;
import com.opengamma.analytics.financial.interestrate.PresentValueCalculator;
import com.opengamma.analytics.financial.interestrate.PresentValueSABRCalculator;
import com.opengamma.analytics.financial.interestrate.PresentValueSABRSensitivityDataBundle;
import com.opengamma.analytics.financial.interestrate.PresentValueSABRSensitivitySABRCalculator;
import com.opengamma.analytics.financial.interestrate.TestsDataSetsSABR;
import com.opengamma.analytics.financial.interestrate.YieldCurveBundle;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CapFloorIbor;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIbor;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.model.option.definition.SABRInterestRateDataBundle;
import com.opengamma.analytics.financial.model.option.definition.SABRInterestRateParameters;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackFunctionData;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackPriceFunction;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.interpolation.LinearInterpolator1D;
import com.opengamma.financial.convention.businessday.BusinessDayConvention;
import com.opengamma.financial.convention.businessday.BusinessDayConventionFactory;
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.DayCountFactory;
import com.opengamma.util.money.Currency;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.tuple.DoublesPair;
/**
* Test related to the pricing and sensitivity of the Ibor cap/floor with the SABR model.
* @deprecated This class tests deprecated functionality.
*/
@Deprecated
public class CapFloorIborSABRMethodTest {
// Details
private static final Period TENOR = Period.ofMonths(3);
private static final int SETTLEMENT_DAYS = 2;
private static final Calendar CALENDAR = new MondayToFridayCalendar("A");
private static final DayCount DAY_COUNT_INDEX = DayCountFactory.INSTANCE.getDayCount("Actual/360");
private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following");
private static final boolean IS_EOM = true;
private static final Currency CUR = Currency.EUR;
private static final IborIndex INDEX = new IborIndex(CUR, TENOR, SETTLEMENT_DAYS, DAY_COUNT_INDEX, BUSINESS_DAY, IS_EOM);
private static final ZonedDateTime FIXING_DATE = DateUtils.getUTCDate(2011, 1, 3);
private static final double NOTIONAL = 1000000; //1m
private static final double STRIKE = 0.04;
private static final boolean IS_CAP = true;
// Definition description
private static final CapFloorIborDefinition CAP_LONG_DEFINITION = CapFloorIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, STRIKE, IS_CAP, CALENDAR);
private static final CouponIborDefinition COUPON_IBOR_DEFINITION = CouponIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, CALENDAR);
private static final CouponFixedDefinition COUPON_STRIKE_DEFINITION = new CouponFixedDefinition(COUPON_IBOR_DEFINITION, STRIKE);
private static final CapFloorIborDefinition CAP_SHORT_DEFINITION = CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE, IS_CAP, CALENDAR);
private static final CapFloorIborDefinition FLOOR_SHORT_DEFINITION = CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE, !IS_CAP, CALENDAR);
// Methods and calculator
private static final CapFloorIborSABRMethod METHOD = CapFloorIborSABRMethod.getInstance();
private static final ParRateCalculator PRC = ParRateCalculator.getInstance();
private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance();
private static final BlackPriceFunction BLACK_FUNCTION = new BlackPriceFunction();
private static final YieldCurveBundle CURVES = TestsDataSetsSABR.createCurves1();
private static final SABRInterestRateParameters SABR_PARAMETER = TestsDataSetsSABR.createSABR1();
private static final SABRInterestRateDataBundle SABR_BUNDLE = new SABRInterestRateDataBundle(SABR_PARAMETER, CURVES);
// To derivative
private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2008, 8, 18);
private static final String FUNDING_CURVE_NAME = "Funding";
private static final String FORWARD_CURVE_NAME = "Forward";
private static final String[] CURVES_NAME = {FUNDING_CURVE_NAME, FORWARD_CURVE_NAME };
private static final CapFloorIbor CAP_LONG = (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
private static final CouponIbor COUPON_IBOR = (CouponIbor) COUPON_IBOR_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
private static final CouponFixed COUPON_STRIKE = COUPON_STRIKE_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
private static final CapFloorIbor CAP_SHORT = (CapFloorIbor) CAP_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
private static final CapFloorIbor FLOOR_SHORT = (CapFloorIbor) FLOOR_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
@Test
/**
* Test the present value using the method with the direct formula (Black with implied volatility).
*/
public void testPresentValue() {
final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves);
final double methodPrice = METHOD.presentValue(CAP_LONG, sabrBundle).getAmount();
final double df = curves.getCurve(FUNDING_CURVE_NAME).getDiscountFactor(CAP_LONG.getPaymentTime());
final double forward = CAP_LONG.accept(PRC, curves);
final double maturity = CAP_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime();
final double volatility = sabrParameter.getVolatility(CAP_LONG.getFixingTime(), maturity, STRIKE, forward);
final BlackFunctionData dataBlack = new BlackFunctionData(forward, df, volatility);
final EuropeanVanillaOption option = new EuropeanVanillaOption(STRIKE, CAP_LONG.getFixingTime(), IS_CAP);
final Function1D<BlackFunctionData, Double> funcBlack = BLACK_FUNCTION.getPriceFunction(option);
final double expectedPrice = funcBlack.evaluate(dataBlack) * CAP_LONG.getNotional() * CAP_LONG.getPaymentYearFraction();
assertEquals("Cap/floor: SABR pricing", expectedPrice, methodPrice, 1E-2);
}
@Test
/**
* Test the present value using the method and the calculator.
*/
public void presentValueMethodVsCalculator() {
final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves);
final double expectedPv = METHOD.presentValue(CAP_LONG, sabrBundle).getAmount();
final PresentValueSABRCalculator pvc = PresentValueSABRCalculator.getInstance();
final double pv = CAP_LONG.accept(pvc, sabrBundle);
assertEquals("Cap/floor SABR pricing: method and calculator", expectedPv, pv, 1E-2);
}
@Test
/**
* Test several present value parities: long/short, cap/floor/forward
*/
public void testPresentValueParity() {
final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves);
final double priceCapLong = METHOD.presentValue(CAP_LONG, sabrBundle).getAmount();
final double priceCapShort = METHOD.presentValue(CAP_SHORT, sabrBundle).getAmount();
assertEquals("Cap/floor - SABR pricing: long/short parity", -priceCapLong, priceCapShort, 1E-2);
final double priceFloorShort = METHOD.presentValue(FLOOR_SHORT, sabrBundle).getAmount();
final double priceIbor = COUPON_IBOR.accept(PVC, curves);
final double priceStrike = COUPON_STRIKE.accept(PVC, curves);
assertEquals("Cap/floor - SABR pricing: cap/floor parity", priceIbor - priceStrike, priceCapLong + priceFloorShort, 1E-2);
}
@Test
/**
* Test the present value rate sensitivity against a finite difference computation. Test sensitivity long/short parity.
*/
public void testPresentValueSensitivity() {
final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves);
final double pv = METHOD.presentValue(CAP_LONG, sabrBundle).getAmount();
InterestRateCurveSensitivity pvsCapLong = METHOD.presentValueSensitivity(CAP_LONG, sabrBundle);
final InterestRateCurveSensitivity pvsCapShort = METHOD.presentValueSensitivity(CAP_SHORT, sabrBundle);
// Long/short parity
final InterestRateCurveSensitivity pvsCapShort_1 = pvsCapShort.multipliedBy(-1);
assertEquals(pvsCapLong.getSensitivities(), pvsCapShort_1.getSensitivities());
// Present value sensitivity comparison with finite difference.
final double deltaTolerance = 1.0E+2;
//Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. Tolerance increased to cope with numerical imprecision of finite difference.
final double deltaShift = 1.0E-7;
pvsCapLong = pvsCapLong.cleaned();
// 1. Forward curve sensitivity
final String bumpedCurveName = "Bumped Curve";
final String[] bumpedCurvesForwardName = {FUNDING_CURVE_NAME, bumpedCurveName };
final CapFloorIbor capBumpedForward = (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, bumpedCurvesForwardName);
final YieldAndDiscountCurve curveForward = curves.getCurve(FORWARD_CURVE_NAME);
final Set<Double> timeForwardSet = new TreeSet<>();
timeForwardSet.add(CAP_LONG.getFixingPeriodStartTime());
timeForwardSet.add(CAP_LONG.getFixingPeriodEndTime());
final int nbForwardDate = timeForwardSet.size();
final List<Double> timeForwardList = new ArrayList<>(timeForwardSet);
Double[] timeForwardArray = new Double[nbForwardDate];
timeForwardArray = timeForwardList.toArray(timeForwardArray);
final double[] yieldsForward = new double[nbForwardDate + 1];
final double[] nodeTimesForward = new double[nbForwardDate + 1];
yieldsForward[0] = curveForward.getInterestRate(0.0);
for (int i = 0; i < nbForwardDate; i++) {
nodeTimesForward[i + 1] = timeForwardArray[i];
yieldsForward[i + 1] = curveForward.getInterestRate(nodeTimesForward[i + 1]);
}
final YieldAndDiscountCurve tempCurveForward = YieldCurve.from(InterpolatedDoublesCurve.fromSorted(nodeTimesForward, yieldsForward, new LinearInterpolator1D()));
final List<DoublesPair> tempForward = pvsCapLong.getSensitivities().get(FORWARD_CURVE_NAME);
final double[] resFwd = new double[nbForwardDate];
for (int i = 0; i < nbForwardDate; i++) {
final YieldAndDiscountCurve bumpedCurveForward = tempCurveForward.withSingleShift(nodeTimesForward[i + 1], deltaShift);
final YieldCurveBundle curvesBumpedForward = new YieldCurveBundle();
curvesBumpedForward.addAll(curves);
curvesBumpedForward.setCurve("Bumped Curve", bumpedCurveForward);
final SABRInterestRateDataBundle sabrBundleBumped = new SABRInterestRateDataBundle(sabrParameter, curvesBumpedForward);
final double bumpedpv = METHOD.presentValue(capBumpedForward, sabrBundleBumped).getAmount();
resFwd[i] = (bumpedpv - pv) / deltaShift;
final DoublesPair pair = tempForward.get(i);
assertEquals("Sensitivity to forward curve: Node " + i, nodeTimesForward[i + 1], pair.getFirst(), 1E-8);
assertEquals("Sensitivity to forward curve: Node " + i, resFwd[i], pair.getSecond(), deltaTolerance);
}
// 2. Funding curve sensitivity
final String[] bumpedCurvesFundingName = {bumpedCurveName, FORWARD_CURVE_NAME };
final CapFloorIbor capBumpedFunding = (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, bumpedCurvesFundingName);
final int nbPayDate = 1;
final YieldAndDiscountCurve curveFunding = curves.getCurve(FUNDING_CURVE_NAME);
final double[] yieldsFunding = new double[nbPayDate + 1];
final double[] nodeTimesFunding = new double[nbPayDate + 1];
yieldsFunding[0] = curveFunding.getInterestRate(0.0);
nodeTimesFunding[1] = CAP_LONG.getPaymentTime();
yieldsFunding[1] = curveFunding.getInterestRate(nodeTimesFunding[1]);
final YieldAndDiscountCurve tempCurveFunding = YieldCurve.from(InterpolatedDoublesCurve.fromSorted(nodeTimesFunding, yieldsFunding, new LinearInterpolator1D()));
final List<DoublesPair> tempFunding = pvsCapLong.getSensitivities().get(FUNDING_CURVE_NAME);
final double[] resDsc = new double[nbPayDate];
for (int i = 0; i < nbPayDate; i++) {
final YieldAndDiscountCurve bumpedCurve = tempCurveFunding.withSingleShift(nodeTimesFunding[i + 1], deltaShift);
final YieldCurveBundle curvesBumped = new YieldCurveBundle();
curvesBumped.addAll(curves);
curvesBumped.setCurve("Bumped Curve", bumpedCurve);
final SABRInterestRateDataBundle sabrBundleBumped = new SABRInterestRateDataBundle(sabrParameter, curvesBumped);
final double bumpedpv = METHOD.presentValue(capBumpedFunding, sabrBundleBumped).getAmount();
resDsc[i] = (bumpedpv - pv) / deltaShift;
final DoublesPair pair = tempFunding.get(i);
assertEquals("Sensitivity to discounting curve: Node " + i, nodeTimesFunding[i + 1], pair.getFirst(), 1E-8);
assertEquals("Sensitivity to discounting curve: Node " + i, resDsc[i], pair.getSecond(), deltaTolerance);
}
}
@Test
/**
* Test the present value SABR parameters sensitivity against a finite difference computation.
*/
public void testPresentValueSABRSensitivity() {
final double pv = METHOD.presentValue(CAP_LONG, SABR_BUNDLE).getAmount();
final PresentValueSABRSensitivityDataBundle pvsCapLong = METHOD.presentValueSABRSensitivity(CAP_LONG, SABR_BUNDLE);
PresentValueSABRSensitivityDataBundle pvsCapShort = METHOD.presentValueSABRSensitivity(CAP_SHORT, SABR_BUNDLE);
// Long/short parity
pvsCapShort = pvsCapShort.multiplyBy(-1.0);
assertEquals(pvsCapShort.getAlpha(), pvsCapLong.getAlpha());
// SABR sensitivity vs finite difference
final double shift = 0.0001;
final double shiftAlpha = 0.00001;
final DoublesPair expectedExpiryTenor = new DoublesPair(CAP_LONG.getFixingTime(), CAP_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime());
// Alpha sensitivity vs finite difference computation
final SABRInterestRateParameters sabrParameterAlphaBumped = TestsDataSetsSABR.createSABR1AlphaBumped(shiftAlpha);
final SABRInterestRateDataBundle sabrBundleAlphaBumped = new SABRInterestRateDataBundle(sabrParameterAlphaBumped, CURVES);
final double pvLongPayerAlphaBumped = METHOD.presentValue(CAP_LONG, sabrBundleAlphaBumped).getAmount();
final double expectedAlphaSensi = (pvLongPayerAlphaBumped - pv) / shiftAlpha;
assertEquals("Number of alpha sensitivity", pvsCapLong.getAlpha().getMap().keySet().size(), 1);
assertEquals("Alpha sensitivity expiry/tenor", pvsCapLong.getAlpha().getMap().keySet().contains(expectedExpiryTenor), true);
assertEquals("Alpha sensitivity value", expectedAlphaSensi, pvsCapLong.getAlpha().getMap().get(expectedExpiryTenor), 2.0E-1);
// Rho sensitivity vs finite difference computation
final SABRInterestRateParameters sabrParameterRhoBumped = TestsDataSetsSABR.createSABR1RhoBumped();
final SABRInterestRateDataBundle sabrBundleRhoBumped = new SABRInterestRateDataBundle(sabrParameterRhoBumped, CURVES);
final double pvLongPayerRhoBumped = METHOD.presentValue(CAP_LONG, sabrBundleRhoBumped).getAmount();
final double expectedRhoSensi = (pvLongPayerRhoBumped - pv) / shift;
assertEquals("Number of rho sensitivity", pvsCapLong.getRho().getMap().keySet().size(), 1);
assertEquals("Rho sensitivity expiry/tenor", pvsCapLong.getRho().getMap().keySet().contains(expectedExpiryTenor), true);
assertEquals("Rho sensitivity value", pvsCapLong.getRho().getMap().get(expectedExpiryTenor), expectedRhoSensi, 1.0E-2);
// Alpha sensitivity vs finite difference computation
final SABRInterestRateParameters sabrParameterNuBumped = TestsDataSetsSABR.createSABR1NuBumped();
final SABRInterestRateDataBundle sabrBundleNuBumped = new SABRInterestRateDataBundle(sabrParameterNuBumped, CURVES);
final double pvLongPayerNuBumped = METHOD.presentValue(CAP_LONG, sabrBundleNuBumped).getAmount();
final double expectedNuSensi = (pvLongPayerNuBumped - pv) / shift;
assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1);
assertTrue("Nu sensitivity expiry/tenor", pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor));
assertEquals("Nu sensitivity value", pvsCapLong.getNu().getMap().get(expectedExpiryTenor), expectedNuSensi, 3.0E-2);
}
@Test
/**
* Tests the present value SABR parameters sensitivity: Method vs Calculator.
*/
public void presentValueSABRSensitivityMethodVsCalculator() {
final PresentValueSABRSensitivityDataBundle pvssMethod = METHOD.presentValueSABRSensitivity(CAP_LONG, SABR_BUNDLE);
final PresentValueSABRSensitivitySABRCalculator calculator = PresentValueSABRSensitivitySABRCalculator.getInstance();
final PresentValueSABRSensitivityDataBundle pvssCalculator = CAP_LONG.accept(calculator, SABR_BUNDLE);
assertEquals("Cap/floor SABR: Present value SABR sensitivity: method vs calculator", pvssMethod, pvssCalculator);
}
}