/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.cms;
import static com.opengamma.strata.basics.currency.Currency.EUR;
import static com.opengamma.strata.basics.date.DayCounts.ACT_360;
import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg;
import static com.opengamma.strata.product.swap.SwapIndices.EUR_EURIBOR_1100_5Y;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.time.LocalDate;
import java.util.function.Function;
import org.testng.annotations.Test;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries;
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.param.CurrencyParameterSensitivity;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.market.surface.InterpolatedNodalSurface;
import com.opengamma.strata.math.impl.integration.RungeKuttaIntegrator1D;
import com.opengamma.strata.pricer.ZeroRateSensitivity;
import com.opengamma.strata.pricer.impl.option.SabrExtrapolationRightFunction;
import com.opengamma.strata.pricer.impl.volatility.smile.SabrFormulaData;
import com.opengamma.strata.pricer.model.SabrInterestRateParameters;
import com.opengamma.strata.pricer.model.SabrVolatilityFormula;
import com.opengamma.strata.pricer.rate.ImmutableRatesProvider;
import com.opengamma.strata.pricer.rate.RatesProvider;
import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator;
import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer;
import com.opengamma.strata.pricer.swaption.SabrParametersSwaptionVolatilities;
import com.opengamma.strata.pricer.swaption.SwaptionSabrRateVolatilityDataSet;
import com.opengamma.strata.pricer.swaption.SwaptionVolatilitiesName;
import com.opengamma.strata.product.cms.CmsPeriod;
import com.opengamma.strata.product.common.BuySell;
import com.opengamma.strata.product.common.PutCall;
import com.opengamma.strata.product.swap.ResolvedSwap;
import com.opengamma.strata.product.swap.ResolvedSwapLeg;
import com.opengamma.strata.product.swap.Swap;
import com.opengamma.strata.product.swap.SwapIndex;
import com.opengamma.strata.product.swap.SwapLegType;
import com.opengamma.strata.product.swap.type.FixedIborSwapConvention;
/**
* Test {@link SabrExtrapolationReplicationCmsPeriodPricer}.
*/
@Test
public class SabrExtrapolationReplicationCmsPeriodPricerTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final LocalDate VALUATION = LocalDate.of(2010, 8, 18);
private static final LocalDate FIXING = LocalDate.of(2020, 4, 24);
private static final LocalDate START = LocalDate.of(2020, 4, 28);
private static final LocalDate END = LocalDate.of(2021, 4, 28);
private static final LocalDate AFTER_FIXING = LocalDate.of(2020, 8, 11);
private static final LocalDate PAYMENT = LocalDate.of(2021, 4, 28);
private static final LocalDate AFTER_PAYMENT = LocalDate.of(2021, 4, 29);
// providers
private static final ImmutableRatesProvider RATES_PROVIDER =
SwaptionSabrRateVolatilityDataSet.getRatesProviderEur(VALUATION);
private static final SabrParametersSwaptionVolatilities VOLATILITIES =
SwaptionSabrRateVolatilityDataSet.getVolatilitiesEur(VALUATION, false);
private static final SabrParametersSwaptionVolatilities VOLATILITIES_SHIFT =
SwaptionSabrRateVolatilityDataSet.getVolatilitiesEur(VALUATION, true);
private static final double SHIFT = VOLATILITIES_SHIFT.getParameters().getShiftSurface().getParameter(0); // constant surface
private static final double OBS_INDEX = 0.0135;
private static final LocalDateDoubleTimeSeries TIME_SERIES = LocalDateDoubleTimeSeries.of(FIXING, OBS_INDEX);
// providers - on fixing date, no time series
private static final ImmutableRatesProvider RATES_PROVIDER_ON_FIX =
SwaptionSabrRateVolatilityDataSet.getRatesProviderEur(FIXING);
private static final SabrParametersSwaptionVolatilities VOLATILITIES_ON_FIX =
SwaptionSabrRateVolatilityDataSet.getVolatilitiesEur(FIXING, true);
// providers - after fixing date, no time series
private static final ImmutableRatesProvider RATES_PROVIDER_NO_TS =
SwaptionSabrRateVolatilityDataSet.getRatesProviderEur(AFTER_FIXING);
private static final SabrParametersSwaptionVolatilities VOLATILITIES_NO_TS =
SwaptionSabrRateVolatilityDataSet.getVolatilitiesEur(AFTER_FIXING, true);
// providers - between fixing date and payment date
private static final ImmutableRatesProvider RATES_PROVIDER_AFTER_FIX =
SwaptionSabrRateVolatilityDataSet.getRatesProviderEur(AFTER_FIXING, TIME_SERIES);
private static final SabrParametersSwaptionVolatilities VOLATILITIES_AFTER_FIX =
SwaptionSabrRateVolatilityDataSet.getVolatilitiesEur(AFTER_FIXING, true);
// providers - on payment date
private static final ImmutableRatesProvider RATES_PROVIDER_ON_PAY =
SwaptionSabrRateVolatilityDataSet.getRatesProviderEur(PAYMENT, TIME_SERIES);
private static final SabrParametersSwaptionVolatilities VOLATILITIES_ON_PAY =
SwaptionSabrRateVolatilityDataSet.getVolatilitiesEur(PAYMENT, true);
// providers - ended
private static final ImmutableRatesProvider RATES_PROVIDER_AFTER_PAY =
SwaptionSabrRateVolatilityDataSet.getRatesProviderEur(AFTER_PAYMENT, TIME_SERIES);
private static final SabrParametersSwaptionVolatilities VOLATILITIES_AFTER_PAY =
SwaptionSabrRateVolatilityDataSet.getVolatilitiesEur(AFTER_PAYMENT, true);
private static final double ACC_FACTOR = ACT_360.relativeYearFraction(START, END);
private static final double NOTIONAL = 10000000; // 10m
private static final double STRIKE = 0.04;
private static final double STRIKE_NEGATIVE = -0.01;
// CMS - buy
private static final CmsPeriod COUPON = createCmsCoupon(true);
private static final CmsPeriod CAPLET = createCmsCaplet(true, STRIKE);
private static final CmsPeriod FLOORLET = createCmsFloorlet(true, STRIKE);
// CMS - sell
private static final CmsPeriod COUPON_SELL = createCmsCoupon(false);
private static final CmsPeriod CAPLET_SELL = createCmsCaplet(false, STRIKE);
private static final CmsPeriod FLOORLET_SELL = createCmsFloorlet(false, STRIKE);
// CMS - zero strikes
private static final CmsPeriod CAPLET_ZERO = createCmsCaplet(true, 0d);
private static final CmsPeriod FLOORLET_ZERO = createCmsFloorlet(true, 0d);
// CMS - negative strikes, to become positive after shift
private static final CmsPeriod CAPLET_NEGATIVE = createCmsCaplet(true, STRIKE_NEGATIVE);
private static final CmsPeriod FLOORLET_NEGATIVE = createCmsFloorlet(true, STRIKE_NEGATIVE);
// CMS - negative strikes, to become zero after shift
private static final CmsPeriod CAPLET_SHIFT = createCmsCaplet(true, -SHIFT);
private static final CmsPeriod FLOORLET_SHIFT = createCmsFloorlet(true, -SHIFT);
private static final double CUT_OFF_STRIKE = 0.10;
private static final double MU = 2.50;
private static final double EPS = 1.0e-5;
private static final double TOL = 1.0e-12;
private static final SabrExtrapolationReplicationCmsPeriodPricer PRICER =
SabrExtrapolationReplicationCmsPeriodPricer.of(CUT_OFF_STRIKE, MU);
private static final RatesFiniteDifferenceSensitivityCalculator FD_CAL =
new RatesFiniteDifferenceSensitivityCalculator(EPS);
private static final DiscountingSwapProductPricer PRICER_SWAP =
DiscountingSwapProductPricer.DEFAULT;
public void test_presentValue_zero() {
CurrencyAmount pv = PRICER.presentValue(COUPON, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvCaplet = PRICER.presentValue(CAPLET_ZERO, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvFloorlet = PRICER.presentValue(FLOORLET_ZERO, RATES_PROVIDER, VOLATILITIES);
assertEquals(pv.getAmount(), pvCaplet.getAmount(), NOTIONAL * TOL);
assertEquals(pvFloorlet.getAmount(), 0d, 2.0d * NOTIONAL * TOL);
CurrencyAmount pvShift = PRICER.presentValue(COUPON, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyAmount pvCapletShift = PRICER.presentValue(CAPLET_SHIFT, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyAmount pvFloorletShift = PRICER.presentValue(FLOORLET_SHIFT, RATES_PROVIDER, VOLATILITIES_SHIFT);
double dfPayment = RATES_PROVIDER.discountFactor(EUR, PAYMENT);
assertEquals(pvShift.getAmount(), pvCapletShift.getAmount() - SHIFT * dfPayment * NOTIONAL * ACC_FACTOR, NOTIONAL * TOL);
assertEquals(pvFloorletShift.getAmount(), 0d, 2.0d * NOTIONAL * TOL);
}
public void test_presentValue_buySell() {
CurrencyAmount pvBuy = PRICER.presentValue(COUPON, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvCapletBuy = PRICER.presentValue(CAPLET, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvFloorletBuy = PRICER.presentValue(FLOORLET, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvSell = PRICER.presentValue(COUPON_SELL, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvCapletSell = PRICER.presentValue(CAPLET_SELL, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvFloorletSell = PRICER.presentValue(FLOORLET_SELL, RATES_PROVIDER, VOLATILITIES);
assertEquals(pvBuy.getAmount(), -pvSell.getAmount(), NOTIONAL * TOL);
assertEquals(pvCapletBuy.getAmount(), -pvCapletSell.getAmount(), NOTIONAL * TOL);
assertEquals(pvFloorletBuy.getAmount(), -pvFloorletSell.getAmount(), NOTIONAL * TOL);
}
public void test_presentValue_afterFix() {
CurrencyAmount pv = PRICER.presentValue(COUPON, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
CurrencyAmount pvCapletOtm = PRICER.presentValue(CAPLET, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
CurrencyAmount pvCapletItm = PRICER.presentValue(CAPLET_NEGATIVE, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
CurrencyAmount pvFloorletItm = PRICER.presentValue(FLOORLET, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
CurrencyAmount pvFloorletOtm = PRICER.presentValue(FLOORLET_NEGATIVE, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
double factor = RATES_PROVIDER_AFTER_FIX.discountFactor(EUR, PAYMENT) * NOTIONAL * COUPON.getYearFraction();
assertEquals(pv.getAmount(), OBS_INDEX * factor, NOTIONAL * TOL);
assertEquals(pvCapletOtm.getAmount(), 0d, NOTIONAL * TOL);
assertEquals(pvCapletItm.getAmount(), (OBS_INDEX - STRIKE_NEGATIVE) * factor, NOTIONAL * TOL);
assertEquals(pvFloorletItm.getAmount(), (STRIKE - OBS_INDEX) * factor, NOTIONAL * TOL);
assertEquals(pvFloorletOtm.getAmount(), 0d, NOTIONAL * TOL);
}
public void test_presentValue_onPayment() {
CurrencyAmount pv = PRICER.presentValue(COUPON, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
CurrencyAmount pvCapletOtm = PRICER.presentValue(CAPLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_AFTER_FIX);
CurrencyAmount pvCapletItm = PRICER.presentValue(CAPLET_NEGATIVE, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
CurrencyAmount pvFloorletItm = PRICER.presentValue(FLOORLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
CurrencyAmount pvFloorletOtm = PRICER.presentValue(FLOORLET_NEGATIVE, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
double factor = NOTIONAL * COUPON.getYearFraction();
assertEquals(pv.getAmount(), OBS_INDEX * factor, NOTIONAL * TOL);
assertEquals(pvCapletOtm.getAmount(), 0d, NOTIONAL * TOL);
assertEquals(pvCapletItm.getAmount(), (OBS_INDEX - STRIKE_NEGATIVE) * factor, NOTIONAL * TOL);
assertEquals(pvFloorletItm.getAmount(), (STRIKE - OBS_INDEX) * factor, NOTIONAL * TOL);
assertEquals(pvFloorletOtm.getAmount(), 0d, NOTIONAL * TOL);
}
public void test_presentValue_afterPayment() {
CurrencyAmount pv = PRICER.presentValue(COUPON, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
CurrencyAmount pvCaplet = PRICER.presentValue(CAPLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
CurrencyAmount pvFloorlet = PRICER.presentValue(FLOORLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
assertEquals(pv, CurrencyAmount.zero(EUR));
assertEquals(pvCaplet, CurrencyAmount.zero(EUR));
assertEquals(pvFloorlet, CurrencyAmount.zero(EUR));
}
public void test_presentValue_afterFix_noTimeSeries() {
assertThrowsIllegalArg(() -> PRICER.presentValue(COUPON, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValue(CAPLET, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValue(FLOORLET, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
}
public void test_presentValue_cap_floor_parity() {
// Cap/Floor parity is not perfect as the cash swaption standard formula is not arbitrage free.
CurrencyAmount pvCap = PRICER.presentValue(CAPLET, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyAmount pvFloor = PRICER.presentValue(FLOORLET, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyAmount pvCpn = PRICER.presentValue(COUPON, RATES_PROVIDER, VOLATILITIES_SHIFT);
double pvStrike = STRIKE * NOTIONAL * ACC_FACTOR * RATES_PROVIDER.discountFactor(EUR, PAYMENT);
assertEquals(pvCap.getAmount() - pvFloor.getAmount(), pvCpn.getAmount() - pvStrike, 1.0E+3);
CurrencyAmount pvCap1 = PRICER.presentValue(CAPLET_NEGATIVE, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyAmount pvFloor1 = PRICER.presentValue(FLOORLET_NEGATIVE, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyAmount pvCpn1 = PRICER.presentValue(COUPON, RATES_PROVIDER, VOLATILITIES_SHIFT);
double pvStrike1 = STRIKE_NEGATIVE * NOTIONAL * ACC_FACTOR * RATES_PROVIDER.discountFactor(EUR, PAYMENT);
assertEquals(pvCap1.getAmount() - pvFloor1.getAmount(), pvCpn1.getAmount() - pvStrike1, 1.0E+3);
CurrencyAmount pvCap2 = PRICER.presentValue(CAPLET, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvFloor2 = PRICER.presentValue(FLOORLET, RATES_PROVIDER, VOLATILITIES);
CurrencyAmount pvCpn2 = PRICER.presentValue(COUPON, RATES_PROVIDER, VOLATILITIES);
double pvStrike2 = STRIKE * NOTIONAL * ACC_FACTOR * RATES_PROVIDER.discountFactor(EUR, PAYMENT);
assertEquals(pvCap2.getAmount() - pvFloor2.getAmount(), pvCpn2.getAmount() - pvStrike2, 1.0E+3);
}
//-------------------------------------------------------------------------
public void test_presentValueSensitivity() {
PointSensitivityBuilder pvPointCoupon = PRICER.presentValueSensitivityRates(COUPON_SELL, RATES_PROVIDER, VOLATILITIES);
CurrencyParameterSensitivities computedCoupon = RATES_PROVIDER
.parameterSensitivity(pvPointCoupon.build());
CurrencyParameterSensitivities expectedCoupon = FD_CAL.sensitivity(
RATES_PROVIDER, p -> PRICER.presentValue(COUPON_SELL, p, VOLATILITIES));
assertTrue(computedCoupon.equalWithTolerance(expectedCoupon, EPS * NOTIONAL * 50d));
PointSensitivityBuilder pvCapPoint = PRICER.presentValueSensitivityRates(CAPLET_SELL, RATES_PROVIDER, VOLATILITIES);
CurrencyParameterSensitivities computedCap = RATES_PROVIDER.parameterSensitivity(pvCapPoint.build());
CurrencyParameterSensitivities expectedCap = FD_CAL.sensitivity(
RATES_PROVIDER, p -> PRICER.presentValue(CAPLET_SELL, p, VOLATILITIES));
assertTrue(computedCap.equalWithTolerance(expectedCap, EPS * NOTIONAL * 50d));
PointSensitivityBuilder pvFloorPoint = PRICER.presentValueSensitivityRates(FLOORLET_SELL, RATES_PROVIDER, VOLATILITIES);
CurrencyParameterSensitivities computedFloor = RATES_PROVIDER.parameterSensitivity(pvFloorPoint.build());
CurrencyParameterSensitivities expectedFloor = FD_CAL.sensitivity(
RATES_PROVIDER, p -> PRICER.presentValue(FLOORLET_SELL, p, VOLATILITIES));
assertTrue(computedFloor.equalWithTolerance(expectedFloor, EPS * NOTIONAL * 10d));
}
public void test_presentValueSensitivity_shift() {
// CurrencyAmount tmp = PRICER.presentValue(COUPON, RATES_PROVIDER, VOLATILITIES_SHIFT);
PointSensitivityBuilder pvPointCoupon = PRICER.presentValueSensitivityRates(COUPON, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyParameterSensitivities computedCoupon = RATES_PROVIDER
.parameterSensitivity(pvPointCoupon.build());
CurrencyParameterSensitivities expectedCoupon = FD_CAL.sensitivity(
RATES_PROVIDER, p -> PRICER.presentValue(COUPON, p, VOLATILITIES_SHIFT));
assertTrue(computedCoupon.equalWithTolerance(expectedCoupon, EPS * NOTIONAL * 50d));
PointSensitivityBuilder pvCapPoint = PRICER.presentValueSensitivityRates(CAPLET_NEGATIVE, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyParameterSensitivities computedCap = RATES_PROVIDER.parameterSensitivity(pvCapPoint.build());
CurrencyParameterSensitivities expectedCap = FD_CAL.sensitivity(
RATES_PROVIDER, p -> PRICER.presentValue(CAPLET_NEGATIVE, p, VOLATILITIES_SHIFT));
assertTrue(computedCap.equalWithTolerance(expectedCap, EPS * NOTIONAL * 50d));
PointSensitivityBuilder pvFloorPoint = PRICER.presentValueSensitivityRates(FLOORLET_NEGATIVE, RATES_PROVIDER, VOLATILITIES_SHIFT);
CurrencyParameterSensitivities computedFloor = RATES_PROVIDER.parameterSensitivity(pvFloorPoint.build());
CurrencyParameterSensitivities expectedFloor = FD_CAL.sensitivity(
RATES_PROVIDER, p -> PRICER.presentValue(FLOORLET_NEGATIVE, p, VOLATILITIES_SHIFT));
assertTrue(computedFloor.equalWithTolerance(expectedFloor, EPS * NOTIONAL * 10d));
}
public void test_presentValueSensitivity_onFix() {
PointSensitivityBuilder pvPointCoupon =
PRICER.presentValueSensitivityRates(COUPON_SELL, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX);
CurrencyParameterSensitivities computedCoupon =
RATES_PROVIDER_ON_FIX.parameterSensitivity(pvPointCoupon.build());
CurrencyParameterSensitivities expectedCoupon =
FD_CAL.sensitivity(RATES_PROVIDER_ON_FIX, p -> PRICER.presentValue(COUPON_SELL, p, VOLATILITIES_ON_FIX));
assertTrue(computedCoupon.equalWithTolerance(expectedCoupon, EPS * NOTIONAL * 50d));
PointSensitivityBuilder pvCapPoint =
PRICER.presentValueSensitivityRates(CAPLET_SELL, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX);
CurrencyParameterSensitivities computedCap =
RATES_PROVIDER_ON_FIX.parameterSensitivity(pvCapPoint.build());
CurrencyParameterSensitivities expectedCap =
FD_CAL.sensitivity(RATES_PROVIDER_ON_FIX, p -> PRICER.presentValue(CAPLET_SELL, p, VOLATILITIES_ON_FIX));
assertTrue(computedCap.equalWithTolerance(expectedCap, EPS * NOTIONAL * 80d));
PointSensitivityBuilder pvFloorPoint =
PRICER.presentValueSensitivityRates(FLOORLET_SELL, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX);
CurrencyParameterSensitivities computedFloor =
RATES_PROVIDER_ON_FIX.parameterSensitivity(pvFloorPoint.build());
CurrencyParameterSensitivities expectedFloor =
FD_CAL.sensitivity(RATES_PROVIDER_ON_FIX, p -> PRICER.presentValue(FLOORLET_SELL, p, VOLATILITIES_ON_FIX));
assertTrue(computedFloor.equalWithTolerance(expectedFloor, EPS * NOTIONAL * 50d));
}
public void test_presentValueSensitivity_afterFix() {
PointSensitivityBuilder pvPointCoupon =
PRICER.presentValueSensitivityRates(COUPON_SELL, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
CurrencyParameterSensitivities computedCoupon =
RATES_PROVIDER_AFTER_FIX.parameterSensitivity(pvPointCoupon.build());
CurrencyParameterSensitivities expectedCoupon =
FD_CAL.sensitivity(RATES_PROVIDER_AFTER_FIX, p -> PRICER.presentValue(COUPON_SELL, p, VOLATILITIES_AFTER_FIX));
assertTrue(computedCoupon.equalWithTolerance(expectedCoupon, EPS * NOTIONAL));
PointSensitivityBuilder pvCapPoint =
PRICER.presentValueSensitivityRates(CAPLET_SELL, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
CurrencyParameterSensitivities computedCap =
RATES_PROVIDER_AFTER_FIX.parameterSensitivity(pvCapPoint.build());
CurrencyParameterSensitivities expectedCap =
FD_CAL.sensitivity(RATES_PROVIDER_AFTER_FIX, p -> PRICER.presentValue(CAPLET_SELL, p, VOLATILITIES_AFTER_FIX));
assertTrue(computedCap.equalWithTolerance(expectedCap, EPS * NOTIONAL));
PointSensitivityBuilder pvFloorPoint =
PRICER.presentValueSensitivityRates(FLOORLET_SELL, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
CurrencyParameterSensitivities computedFloor =
RATES_PROVIDER_AFTER_FIX.parameterSensitivity(pvFloorPoint.build());
CurrencyParameterSensitivities expectedFloor =
FD_CAL.sensitivity(RATES_PROVIDER_AFTER_FIX, p -> PRICER.presentValue(FLOORLET_SELL, p, VOLATILITIES_AFTER_FIX));
assertTrue(computedFloor.equalWithTolerance(expectedFloor, EPS * NOTIONAL));
}
public void test_presentValueSensitivity_onPayment() {
PointSensitivityBuilder pvSensi = PRICER
.presentValueSensitivityRates(COUPON, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
PointSensitivityBuilder pvSensiCapletOtm =
PRICER.presentValueSensitivityRates(CAPLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_AFTER_FIX);
PointSensitivityBuilder pvSensiCapletItm =
PRICER.presentValueSensitivityRates(CAPLET_NEGATIVE, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
PointSensitivityBuilder pvSensiFloorletItm =
PRICER.presentValueSensitivityRates(FLOORLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
PointSensitivityBuilder pvSensiFloorletOtm =
PRICER.presentValueSensitivityRates(FLOORLET_NEGATIVE, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
double paymentTime = RATES_PROVIDER_ON_PAY.discountFactors(EUR).relativeYearFraction(PAYMENT);
PointSensitivityBuilder expected = ZeroRateSensitivity.of(EUR, paymentTime, -0d);
assertEquals(pvSensi, expected);
assertEquals(pvSensiCapletOtm, expected);
assertEquals(pvSensiCapletItm, expected);
assertEquals(pvSensiFloorletItm, expected);
assertEquals(pvSensiFloorletOtm, expected);
}
public void test_presentValueSensitivity_afterFix_noTimeSeries() {
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityRates(COUPON, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityRates(CAPLET, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityRates(FLOORLET, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
}
public void test_presentValueSensitivity_afterPayment() {
PointSensitivityBuilder pt = PRICER.presentValueSensitivityRates(COUPON, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
PointSensitivityBuilder ptCap =
PRICER.presentValueSensitivityRates(CAPLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
PointSensitivityBuilder ptFloor =
PRICER.presentValueSensitivityRates(FLOORLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
assertEquals(pt, PointSensitivityBuilder.none());
assertEquals(ptCap, PointSensitivityBuilder.none());
assertEquals(ptFloor, PointSensitivityBuilder.none());
}
//-------------------------------------------------------------------------
public void test_presentValueSensitivitySabrParameter() {
testPresentValueSensitivitySabrParameter(
COUPON_SELL, CAPLET_SELL, FLOORLET_SELL, RATES_PROVIDER, VOLATILITIES);
testPresentValueSensitivitySabrParameter(
COUPON, CAPLET_NEGATIVE, FLOORLET_NEGATIVE, RATES_PROVIDER, VOLATILITIES_SHIFT);
testPresentValueSensitivitySabrParameter(
COUPON_SELL, CAPLET_SELL, FLOORLET_SELL, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX);
}
public void test_presentValueSensitivitySabrParameter_afterFix() {
PointSensitivityBuilder pvCouponPoint =
PRICER.presentValueSensitivityModelParamsSabr(COUPON_SELL, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
PointSensitivityBuilder pvCapPoint =
PRICER.presentValueSensitivityModelParamsSabr(CAPLET_SELL, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
PointSensitivityBuilder pvFloorPoint =
PRICER.presentValueSensitivityModelParamsSabr(FLOORLET_SELL, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
assertEquals(pvCouponPoint, PointSensitivityBuilder.none());
assertEquals(pvCapPoint, PointSensitivityBuilder.none());
assertEquals(pvFloorPoint, PointSensitivityBuilder.none());
}
public void test_presentValueSensitivitySabrParameter_onPayment() {
PointSensitivityBuilder pvSensi = PRICER
.presentValueSensitivityModelParamsSabr(COUPON, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
PointSensitivityBuilder pvSensiCapletOtm =
PRICER.presentValueSensitivityModelParamsSabr(CAPLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_AFTER_FIX);
PointSensitivityBuilder pvSensiCapletItm =
PRICER.presentValueSensitivityModelParamsSabr(CAPLET_NEGATIVE, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
PointSensitivityBuilder pvSensiFloorletItm =
PRICER.presentValueSensitivityModelParamsSabr(FLOORLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
PointSensitivityBuilder pvSensiFloorletOtm =
PRICER.presentValueSensitivityModelParamsSabr(FLOORLET_NEGATIVE, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
assertEquals(pvSensi, PointSensitivityBuilder.none());
assertEquals(pvSensiCapletOtm, PointSensitivityBuilder.none());
assertEquals(pvSensiCapletItm, PointSensitivityBuilder.none());
assertEquals(pvSensiFloorletItm, PointSensitivityBuilder.none());
assertEquals(pvSensiFloorletOtm, PointSensitivityBuilder.none());
}
public void test_presentValueSensitivitySabrParameter_afterFix_noTimeSeries() {
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityModelParamsSabr(COUPON, RATES_PROVIDER_NO_TS,
VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityModelParamsSabr(CAPLET, RATES_PROVIDER_NO_TS,
VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityModelParamsSabr(FLOORLET, RATES_PROVIDER_NO_TS,
VOLATILITIES_NO_TS));
}
public void test_presentValueSensitivitySabrParameter_afterPayment() {
PointSensitivityBuilder sensi =
PRICER.presentValueSensitivityModelParamsSabr(COUPON, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
PointSensitivityBuilder sensiCap =
PRICER.presentValueSensitivityModelParamsSabr(CAPLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
PointSensitivityBuilder sensiFloor =
PRICER.presentValueSensitivityModelParamsSabr(FLOORLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
assertEquals(sensi, PointSensitivityBuilder.none());
assertEquals(sensiCap, PointSensitivityBuilder.none());
assertEquals(sensiFloor, PointSensitivityBuilder.none());
}
public void test_adjusted_forward_rate() {
CmsPeriod coupon1 = COUPON.toBuilder().notional(1.0).yearFraction(1.0).build();
CurrencyAmount pvBuy = PRICER.presentValue(coupon1, RATES_PROVIDER, VOLATILITIES);
double df = RATES_PROVIDER.discountFactor(EUR, PAYMENT);
double adjustedForwardRateExpected = pvBuy.getAmount() / df;
double adjustedForwardRateComputed = PRICER.adjustedForwardRate(COUPON, RATES_PROVIDER, VOLATILITIES);
assertEquals(adjustedForwardRateComputed, adjustedForwardRateExpected, TOL);
}
public void test_adjustment_forward_rate() {
double adjustedForwardRateComputed = PRICER.adjustedForwardRate(COUPON, RATES_PROVIDER, VOLATILITIES);
double forward = PRICER_SWAP.parRate(COUPON.getUnderlyingSwap(), RATES_PROVIDER);
double adjustmentComputed = PRICER.adjustmentToForwardRate(COUPON, RATES_PROVIDER, VOLATILITIES);
assertEquals(adjustmentComputed, adjustedForwardRateComputed - forward, TOL);
}
public void test_adjusted_forward_rate_cap_floor() {
double adjustedForwardRateCoupon = PRICER.adjustedForwardRate(COUPON, RATES_PROVIDER, VOLATILITIES);
double adjustedForwardRateFloor = PRICER.adjustedForwardRate(FLOORLET, RATES_PROVIDER, VOLATILITIES);
assertEquals(adjustedForwardRateCoupon, adjustedForwardRateFloor, TOL);
double adjustedForwardRateCap = PRICER.adjustedForwardRate(CAPLET, RATES_PROVIDER, VOLATILITIES);
assertEquals(adjustedForwardRateCoupon, adjustedForwardRateCap, TOL);
}
public void test_adjusted_forward_rate_afterFix() {
double adjustedForward = PRICER.adjustedForwardRate(COUPON, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
assertEquals(adjustedForward, OBS_INDEX , TOL);
}
public void test_adjusted_rate_error() {
assertThrowsIllegalArg(() -> PRICER.adjustmentToForwardRate(COUPON, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX));
}
//-------------------------------------------------------------------------
private static final CmsPeriod CAPLET_UP = createCmsCaplet(true, STRIKE + EPS);
private static final CmsPeriod CAPLET_DW = createCmsCaplet(true, STRIKE - EPS);
private static final CmsPeriod FLOORLET_UP = createCmsFloorlet(true, STRIKE + EPS);
private static final CmsPeriod FLOORLET_DW = createCmsFloorlet(true, STRIKE - EPS);
public void test_presentValueSensitivityStrike() {
double computedCaplet = PRICER.presentValueSensitivityStrike(CAPLET, RATES_PROVIDER, VOLATILITIES);
double expectedCaplet = 0.5 * (PRICER.presentValue(CAPLET_UP, RATES_PROVIDER, VOLATILITIES).getAmount()
- PRICER.presentValue(CAPLET_DW, RATES_PROVIDER, VOLATILITIES).getAmount()) / EPS;
assertEquals(computedCaplet, expectedCaplet, NOTIONAL * EPS);
double computedFloorlet = PRICER.presentValueSensitivityStrike(FLOORLET, RATES_PROVIDER, VOLATILITIES);
double expectedFloorlet = 0.5 * (PRICER.presentValue(FLOORLET_UP, RATES_PROVIDER, VOLATILITIES).getAmount()
- PRICER.presentValue(FLOORLET_DW, RATES_PROVIDER, VOLATILITIES).getAmount()) / EPS;
assertEquals(computedFloorlet, expectedFloorlet, NOTIONAL * EPS);
}
public void test_presentValueSensitivityStrike_shift() {
double computedCaplet = PRICER.presentValueSensitivityStrike(CAPLET_NEGATIVE, RATES_PROVIDER, VOLATILITIES_SHIFT);
CmsPeriod capletUp = createCmsCaplet(true, STRIKE_NEGATIVE + EPS);
CmsPeriod capletDw = createCmsCaplet(true, STRIKE_NEGATIVE - EPS);
double expectedCaplet = 0.5 * (PRICER.presentValue(capletUp, RATES_PROVIDER, VOLATILITIES_SHIFT).getAmount()
- PRICER.presentValue(capletDw, RATES_PROVIDER, VOLATILITIES_SHIFT).getAmount()) / EPS;
assertEquals(computedCaplet, expectedCaplet, NOTIONAL * EPS);
double computedFloorlet = PRICER.presentValueSensitivityStrike(FLOORLET_NEGATIVE, RATES_PROVIDER, VOLATILITIES_SHIFT);
CmsPeriod floorletUp = createCmsFloorlet(true, STRIKE_NEGATIVE + EPS);
CmsPeriod floorletDw = createCmsFloorlet(true, STRIKE_NEGATIVE - EPS);
double expectedFloorlet = 0.5 * (PRICER.presentValue(floorletUp, RATES_PROVIDER, VOLATILITIES_SHIFT).getAmount()
- PRICER.presentValue(floorletDw, RATES_PROVIDER, VOLATILITIES_SHIFT).getAmount()) / EPS;
assertEquals(computedFloorlet, expectedFloorlet, NOTIONAL * EPS);
}
public void test_presentValueSensitivityStrike_onFix() {
double computedCaplet = PRICER.presentValueSensitivityStrike(CAPLET, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX);
double expectedCaplet = 0.5 *
(PRICER.presentValue(CAPLET_UP, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX).getAmount()
- PRICER.presentValue(CAPLET_DW, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX).getAmount()) / EPS;
assertEquals(computedCaplet, expectedCaplet, NOTIONAL * EPS);
double computedFloorlet = PRICER
.presentValueSensitivityStrike(FLOORLET, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX);
double expectedFloorlet = 0.5 *
(PRICER.presentValue(FLOORLET_UP, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX).getAmount()
- PRICER.presentValue(FLOORLET_DW, RATES_PROVIDER_ON_FIX, VOLATILITIES_ON_FIX).getAmount()) / EPS;
assertEquals(computedFloorlet, expectedFloorlet, NOTIONAL * EPS * 10d);
}
public void test_presentValueSensitivityStrike_afterFix() {
double cmpCapletOtm =
PRICER.presentValueSensitivityStrike(CAPLET, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
double cmpCapletItm =
PRICER.presentValueSensitivityStrike(CAPLET_NEGATIVE, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
double cmpFloorletItm =
PRICER.presentValueSensitivityStrike(FLOORLET, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
double cmpFloorletOtm =
PRICER.presentValueSensitivityStrike(FLOORLET_NEGATIVE, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX);
double expCapletOtm = (PRICER.presentValue(CAPLET_UP, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX).getAmount()
- PRICER.presentValue(CAPLET_DW, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX).getAmount()) * 0.5 / EPS;
double expCapletItm = (PRICER.presentValue(createCmsCaplet(true, STRIKE_NEGATIVE + EPS), RATES_PROVIDER_AFTER_FIX,
VOLATILITIES_AFTER_FIX).getAmount() - PRICER.presentValue(createCmsCaplet(true, STRIKE_NEGATIVE - EPS),
RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX).getAmount()) * 0.5 / EPS;
double expFloorletItm = (PRICER.presentValue(FLOORLET_UP, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX).getAmount()
- PRICER.presentValue(FLOORLET_DW, RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX).getAmount()) * 0.5 / EPS;
double expFloorletOtm = (PRICER.presentValue(createCmsFloorlet(true, STRIKE_NEGATIVE + EPS), RATES_PROVIDER_AFTER_FIX,
VOLATILITIES_AFTER_FIX).getAmount() - PRICER.presentValue(createCmsFloorlet(true, STRIKE_NEGATIVE - EPS),
RATES_PROVIDER_AFTER_FIX, VOLATILITIES_AFTER_FIX).getAmount()) * 0.5 / EPS;
assertEquals(cmpCapletOtm, expCapletOtm, NOTIONAL * EPS);
assertEquals(cmpCapletItm, expCapletItm, NOTIONAL * EPS);
assertEquals(cmpFloorletOtm, expFloorletOtm, NOTIONAL * EPS);
assertEquals(cmpFloorletItm, expFloorletItm, NOTIONAL * EPS);
}
public void test_presentValueSensitivityStrike_onPayment() {
double computedCaplet = PRICER.presentValueSensitivityStrike(CAPLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
double expectedCaplet = (PRICER.presentValue(CAPLET_UP, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY).getAmount()
- PRICER.presentValue(CAPLET_DW, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY).getAmount()) * 0.5 / EPS;
assertEquals(computedCaplet, expectedCaplet, NOTIONAL * EPS);
double computedFloorlet = PRICER
.presentValueSensitivityStrike(FLOORLET, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY);
double expectedFloorlet = (PRICER.presentValue(FLOORLET_UP, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY).getAmount()
- PRICER.presentValue(FLOORLET_DW, RATES_PROVIDER_ON_PAY, VOLATILITIES_ON_PAY).getAmount()) * 0.5 / EPS;
assertEquals(computedFloorlet, expectedFloorlet, NOTIONAL * EPS);
}
public void test_presentValueSensitivityStrike_afterFix_noTimeSeries() {
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityStrike(COUPON, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityStrike(CAPLET, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityStrike(FLOORLET, RATES_PROVIDER_NO_TS, VOLATILITIES_NO_TS));
}
public void test_presentValueSensitivityStrike_afterPayment() {
double sensiCap = PRICER.presentValueSensitivityStrike(CAPLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
double sensiFloor = PRICER.presentValueSensitivityStrike(FLOORLET, RATES_PROVIDER_AFTER_PAY, VOLATILITIES_AFTER_PAY);
assertEquals(sensiCap, 0d);
assertEquals(sensiFloor, 0d);
}
public void test_presentValueSensitivityStrike_coupon() {
assertThrowsIllegalArg(() -> PRICER.presentValueSensitivityStrike(COUPON, RATES_PROVIDER, VOLATILITIES));
}
//-------------------------------------------------------------------------
private void testPresentValueSensitivitySabrParameter(CmsPeriod coupon, CmsPeriod caplet, CmsPeriod foorlet,
RatesProvider ratesProvider, SabrParametersSwaptionVolatilities volatilities) {
PointSensitivities pvPointCoupon =
PRICER.presentValueSensitivityModelParamsSabr(coupon, ratesProvider, volatilities).build();
CurrencyParameterSensitivities computedCoupon =
volatilities.parameterSensitivity(pvPointCoupon);
PointSensitivities pvCapPoint =
PRICER.presentValueSensitivityModelParamsSabr(caplet, ratesProvider, volatilities).build();
CurrencyParameterSensitivities computedCap =
volatilities.parameterSensitivity(pvCapPoint);
PointSensitivities pvFloorPoint =
PRICER.presentValueSensitivityModelParamsSabr(foorlet, ratesProvider, volatilities).build();
CurrencyParameterSensitivities computedFloor =
volatilities.parameterSensitivity(pvFloorPoint);
SabrInterestRateParameters sabr = volatilities.getParameters();
// alpha surface
InterpolatedNodalSurface surfaceAlpha = (InterpolatedNodalSurface) sabr.getAlphaSurface();
CurrencyParameterSensitivity sensiCouponAlpha = computedCoupon.getSensitivity(surfaceAlpha.getName(), EUR);
int nParamsAlpha = surfaceAlpha.getParameterCount();
for (int i = 0; i < nParamsAlpha; ++i) {
InterpolatedNodalSurface[] bumpedSurfaces = bumpSurface(surfaceAlpha, i);
SabrInterestRateParameters sabrUp = SabrInterestRateParameters.of(bumpedSurfaces[0], sabr.getBetaSurface(),
sabr.getRhoSurface(), sabr.getNuSurface(), sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
SabrInterestRateParameters sabrDw = SabrInterestRateParameters.of(bumpedSurfaces[1], sabr.getBetaSurface(),
sabr.getRhoSurface(), sabr.getNuSurface(), sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
testSensitivityValue(
coupon, caplet, foorlet, ratesProvider, i,
sensiCouponAlpha.getSensitivity(),
computedCap.getSensitivity(surfaceAlpha.getName(), EUR).getSensitivity(),
computedFloor.getSensitivity(surfaceAlpha.getName(), EUR).getSensitivity(),
replaceSabrParameters(sabrUp, volatilities),
replaceSabrParameters(sabrDw, volatilities));
}
// beta surface
InterpolatedNodalSurface surfaceBeta = (InterpolatedNodalSurface) sabr.getBetaSurface();
CurrencyParameterSensitivity sensiCouponBeta = computedCoupon.getSensitivity(surfaceBeta.getName(), EUR);
int nParamsBeta = surfaceBeta.getParameterCount();
for (int i = 0; i < nParamsBeta; ++i) {
InterpolatedNodalSurface[] bumpedSurfaces = bumpSurface(surfaceBeta, i);
SabrInterestRateParameters sabrUp = SabrInterestRateParameters.of(sabr.getAlphaSurface(), bumpedSurfaces[0],
sabr.getRhoSurface(), sabr.getNuSurface(), sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
SabrInterestRateParameters sabrDw = SabrInterestRateParameters.of(sabr.getAlphaSurface(), bumpedSurfaces[1],
sabr.getRhoSurface(), sabr.getNuSurface(), sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
testSensitivityValue(
coupon, caplet, foorlet, ratesProvider, i,
sensiCouponBeta.getSensitivity(),
computedCap.getSensitivity(surfaceBeta.getName(), EUR).getSensitivity(),
computedFloor.getSensitivity(surfaceBeta.getName(), EUR).getSensitivity(),
replaceSabrParameters(sabrUp, volatilities),
replaceSabrParameters(sabrDw, volatilities));
}
// rho surface
InterpolatedNodalSurface surfaceRho = (InterpolatedNodalSurface) sabr.getRhoSurface();
CurrencyParameterSensitivity sensiCouponRho = computedCoupon.getSensitivity(surfaceRho.getName(), EUR);
int nParamsRho = surfaceRho.getParameterCount();
for (int i = 0; i < nParamsRho; ++i) {
InterpolatedNodalSurface[] bumpedSurfaces = bumpSurface(surfaceRho, i);
SabrInterestRateParameters sabrUp = SabrInterestRateParameters.of(sabr.getAlphaSurface(), sabr.getBetaSurface(),
bumpedSurfaces[0], sabr.getNuSurface(), sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
SabrInterestRateParameters sabrDw = SabrInterestRateParameters.of(sabr.getAlphaSurface(), sabr.getBetaSurface(),
bumpedSurfaces[1], sabr.getNuSurface(), sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
testSensitivityValue(
coupon, caplet, foorlet, ratesProvider, i,
sensiCouponRho.getSensitivity(),
computedCap.getSensitivity(surfaceRho.getName(), EUR).getSensitivity(),
computedFloor.getSensitivity(surfaceRho.getName(), EUR).getSensitivity(),
replaceSabrParameters(sabrUp, volatilities),
replaceSabrParameters(sabrDw, volatilities));
}
// nu surface
InterpolatedNodalSurface surfaceNu = (InterpolatedNodalSurface) sabr.getNuSurface();
CurrencyParameterSensitivity sensiCouponNu = computedCoupon.getSensitivity(surfaceNu.getName(), EUR);
int nParamsNu = surfaceNu.getParameterCount();
for (int i = 0; i < nParamsNu; ++i) {
InterpolatedNodalSurface[] bumpedSurfaces = bumpSurface(surfaceNu, i);
SabrInterestRateParameters sabrUp = SabrInterestRateParameters.of(sabr.getAlphaSurface(), sabr.getBetaSurface(),
sabr.getRhoSurface(), bumpedSurfaces[0], sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
SabrInterestRateParameters sabrDw = SabrInterestRateParameters.of(sabr.getAlphaSurface(), sabr.getBetaSurface(),
sabr.getRhoSurface(), bumpedSurfaces[1], sabr.getShiftSurface(), SabrVolatilityFormula.hagan());
testSensitivityValue(
coupon, caplet, foorlet, ratesProvider, i,
sensiCouponNu.getSensitivity(),
computedCap.getSensitivity(surfaceNu.getName(), EUR).getSensitivity(),
computedFloor.getSensitivity(surfaceNu.getName(), EUR).getSensitivity(),
replaceSabrParameters(sabrUp, volatilities),
replaceSabrParameters(sabrDw, volatilities));
}
}
private InterpolatedNodalSurface[] bumpSurface(InterpolatedNodalSurface surface, int position) {
DoubleArray zValues = surface.getZValues();
InterpolatedNodalSurface surfaceUp = surface.withZValues(zValues.with(position, zValues.get(position) + EPS));
InterpolatedNodalSurface surfaceDw = surface.withZValues(zValues.with(position, zValues.get(position) - EPS));
return new InterpolatedNodalSurface[] {surfaceUp, surfaceDw };
}
private SabrParametersSwaptionVolatilities replaceSabrParameters(
SabrInterestRateParameters sabrParams,
SabrParametersSwaptionVolatilities orgVols) {
return SabrParametersSwaptionVolatilities.of(
SwaptionVolatilitiesName.of("Test-SABR"), orgVols.getConvention(), orgVols.getValuationDateTime(), sabrParams);
}
private void testSensitivityValue(
CmsPeriod coupon, CmsPeriod caplet, CmsPeriod floorlet, RatesProvider ratesProvider, int index,
DoubleArray computedCouponSensi, DoubleArray computedCapSensi, DoubleArray computedFloorSensi,
SabrParametersSwaptionVolatilities volsUp, SabrParametersSwaptionVolatilities volsDw) {
double expectedCoupon = 0.5 * (PRICER.presentValue(coupon, ratesProvider, volsUp).getAmount()
- PRICER.presentValue(coupon, ratesProvider, volsDw).getAmount()) / EPS;
double expectedCap = 0.5 * (PRICER.presentValue(caplet, ratesProvider, volsUp).getAmount()
- PRICER.presentValue(caplet, ratesProvider, volsDw).getAmount()) / EPS;
double expectedFloor = 0.5 * (PRICER.presentValue(floorlet, ratesProvider, volsUp).getAmount()
- PRICER.presentValue(floorlet, ratesProvider, volsDw).getAmount()) / EPS;
assertEquals(computedCouponSensi.get(index), expectedCoupon, EPS * NOTIONAL * 10d);
assertEquals(computedCapSensi.get(index), expectedCap, EPS * NOTIONAL * 10d);
assertEquals(computedFloorSensi.get(index), expectedFloor, EPS * NOTIONAL * 10d);
}
private static CmsPeriod createCmsCoupon(boolean isBuy) {
double notional = isBuy ? NOTIONAL : -NOTIONAL;
return CmsPeriod.builder()
.dayCount(ACT_360)
.currency(EUR)
.index(EUR_EURIBOR_1100_5Y)
.startDate(START)
.endDate(END)
.fixingDate(FIXING)
.notional(notional)
.paymentDate(PAYMENT)
.yearFraction(ACC_FACTOR)
.underlyingSwap(createUnderlyingSwap(FIXING))
.build();
}
private static CmsPeriod createCmsCaplet(boolean isBuy, double strike) {
double notional = isBuy ? NOTIONAL : -NOTIONAL;
return CmsPeriod.builder()
.dayCount(ACT_360)
.currency(EUR)
.index(EUR_EURIBOR_1100_5Y)
.startDate(START)
.endDate(END)
.fixingDate(FIXING)
.notional(notional)
.paymentDate(PAYMENT)
.yearFraction(ACC_FACTOR)
.caplet(strike)
.underlyingSwap(createUnderlyingSwap(FIXING))
.build();
}
private static CmsPeriod createCmsFloorlet(boolean isBuy, double strike) {
double notional = isBuy ? NOTIONAL : -NOTIONAL;
return CmsPeriod.builder()
.dayCount(ACT_360)
.currency(EUR)
.index(EUR_EURIBOR_1100_5Y)
.startDate(START)
.endDate(END)
.fixingDate(FIXING)
.notional(notional)
.paymentDate(PAYMENT)
.yearFraction(ACC_FACTOR)
.floorlet(strike)
.underlyingSwap(createUnderlyingSwap(FIXING))
.build();
}
// creates and resolves the underlying swap
private static ResolvedSwap createUnderlyingSwap(LocalDate fixingDate) {
FixedIborSwapConvention conv = EUR_EURIBOR_1100_5Y.getTemplate().getConvention();
LocalDate effectiveDate = conv.calculateSpotDateFromTradeDate(fixingDate, REF_DATA);
LocalDate maturityDate = effectiveDate.plus(EUR_EURIBOR_1100_5Y.getTemplate().getTenor());
Swap swap = conv.toTrade(fixingDate, effectiveDate, maturityDate, BuySell.BUY, 1d, 1d).getProduct();
return swap.resolve(REF_DATA);
}
//-------------------------------------------------------------------------
private static final double TOLERANCE_K_P = 1.0E-8;
private static final double TOLERANCE_K_PP = 1.0E-4;
private static final double TOLERANCE_PV = 1.0E+0;
/* Check that the internal function used in the integrant (h, G and k in the documentation) are correctly implemented. */
public void integrant_internal() {
SwapIndex index = CAPLET.getIndex();
LocalDate effectiveDate = CAPLET.getUnderlyingSwap().getStartDate();
ResolvedSwap expanded = CAPLET.getUnderlyingSwap();
double tenor = VOLATILITIES_SHIFT.tenor(effectiveDate, CAPLET.getUnderlyingSwap().getEndDate());
double theta = VOLATILITIES_SHIFT.relativeTime(
CAPLET.getFixingDate().atTime(index.getFixingTime()).atZone(index.getFixingZone()));
double delta = index.getTemplate().getConvention().getFixedLeg()
.getDayCount().relativeYearFraction(effectiveDate, PAYMENT);
double S0 = PRICER_SWAP.parRate(COUPON.getUnderlyingSwap(), RATES_PROVIDER);
CmsIntegrantProvider integrant = new CmsIntegrantProvider(CAPLET, expanded, STRIKE, tenor, theta,
S0, -delta, VOLATILITIES_SHIFT, CUT_OFF_STRIKE, MU);
// Integrant internal
double h = integrant.h(STRIKE);
double hExpected = Math.pow(1 + STRIKE, -delta);
assertEquals(h, hExpected, TOLERANCE_K_P);
double g = integrant.g(STRIKE);
double gExpected = (1.0 - 1.0 / Math.pow(1 + STRIKE, tenor)) / STRIKE;
assertEquals(g, gExpected, TOLERANCE_K_P);
double kExpected = integrant.h(STRIKE) / integrant.g(STRIKE);
double k = integrant.k(STRIKE);
assertEquals(k, kExpected, TOLERANCE_K_P);
double shiftFd = 1.0E-5;
double kP = integrant.h(STRIKE + shiftFd) / integrant.g(STRIKE + shiftFd);
double kM = integrant.h(STRIKE - shiftFd) / integrant.g(STRIKE - shiftFd);
double[] kpkpp = integrant.kpkpp(STRIKE);
assertEquals(kpkpp[0], (kP - kM) / (2 * shiftFd), TOLERANCE_K_P);
assertEquals(kpkpp[1], (kP + kM - 2 * k) / (shiftFd * shiftFd), TOLERANCE_K_PP);
}
/* Check the present value v. */
public void test_presentValue_replication_cap() {
SwapIndex index = CAPLET.getIndex();
LocalDate effectiveDate = CAPLET.getUnderlyingSwap().getStartDate();
ResolvedSwap expanded = CAPLET.getUnderlyingSwap();
double tenor = VOLATILITIES.tenor(effectiveDate, CAPLET.getUnderlyingSwap().getEndDate());
double theta = VOLATILITIES.relativeTime(
CAPLET.getFixingDate().atTime(index.getFixingTime()).atZone(index.getFixingZone()));
double delta = index.getTemplate().getConvention().getFixedLeg()
.getDayCount().relativeYearFraction(effectiveDate, PAYMENT);
double ptp = RATES_PROVIDER.discountFactor(EUR, PAYMENT);
double S0 = PRICER_SWAP.parRate(COUPON.getUnderlyingSwap(), RATES_PROVIDER);
CmsIntegrantProvider integrant = new CmsIntegrantProvider(CAPLET, expanded, STRIKE, tenor, theta,
S0, -delta, VOLATILITIES_SHIFT, CUT_OFF_STRIKE, MU);
// Strike part
double h_1S0 = 1.0 / integrant.h(S0);
double gS0 = integrant.g(S0);
double kK = integrant.k(STRIKE);
double bsS0 = integrant.bs(STRIKE);
double strikePart = ptp * h_1S0 * gS0 * kK * bsS0;
// Integral part
RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(1.0E-7, 1.0E-10, 10);
double integralPart = ptp * integrator.integrate(integrant.integrant(), STRIKE, 100.0);
double pvExpected = (strikePart + integralPart) * NOTIONAL * ACC_FACTOR;
CurrencyAmount pvComputed = PRICER.presentValue(CAPLET, RATES_PROVIDER, VOLATILITIES_SHIFT);
assertEquals(pvComputed.getAmount(), pvExpected, TOLERANCE_PV);
}
//---------------------------------------------------------------------
public void test_explainPresentValue() {
ExplainMapBuilder builder = ExplainMap.builder();
PRICER.explainPresentValue(FLOORLET, RATES_PROVIDER, VOLATILITIES, builder);
ExplainMap explain = builder.build();
//Test a CMS Floorlet Period.
assertEquals(explain.get(ExplainKey.ENTRY_TYPE).get(), "CmsFloorletPeriod");
assertEquals(explain.get(ExplainKey.STRIKE_VALUE).get(), 0.04d);
assertEquals(explain.get(ExplainKey.NOTIONAL).get().getAmount(), 10000000d);
assertEquals(explain.get(ExplainKey.PAYMENT_DATE).get(), LocalDate.of(2021, 04, 28));
assertEquals(explain.get(ExplainKey.DISCOUNT_FACTOR).get(), 0.8518053333230845d);
assertEquals(explain.get(ExplainKey.START_DATE).get(), LocalDate.of(2020, 04, 28));
assertEquals(explain.get(ExplainKey.END_DATE).get(), LocalDate.of(2021, 04, 28));
assertEquals(explain.get(ExplainKey.FIXING_DATE).get(), LocalDate.of(2020, 04, 24));
assertEquals(explain.get(ExplainKey.ACCRUAL_YEAR_FRACTION).get(), 1.0138888888888888d);
double forwardSwapRate = PRICER_SWAP.parRate(FLOORLET.getUnderlyingSwap(), RATES_PROVIDER);
assertEquals(explain.get(ExplainKey.FORWARD_RATE).get(), forwardSwapRate);
CurrencyAmount pv = PRICER.presentValue(FLOORLET, RATES_PROVIDER, VOLATILITIES);
assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get(), pv);
double adjustedForwardRate = PRICER.adjustedForwardRate(FLOORLET, RATES_PROVIDER, VOLATILITIES);
assertEquals(explain.get(ExplainKey.CONVEXITY_ADJUSTED_RATE).get(), adjustedForwardRate);
}
//-------------------------------------------------------------------------
/** Simplified integrant for testing; only cap; underlying with annual payments */
private class CmsIntegrantProvider {
private final int nbFixedPeriod;
private final double eta;
private final double strike;
private final double shift;
private final double factor;
private final SabrExtrapolationRightFunction sabrExtrapolation;
public CmsIntegrantProvider(
CmsPeriod cmsPeriod,
ResolvedSwap swap,
double strike,
double tenor,
double timeToExpiry,
double forward,
double eta,
SabrParametersSwaptionVolatilities swaptionVolatilities,
double cutOffStrike,
double mu) {
ResolvedSwapLeg fixedLeg = swap.getLegs(SwapLegType.FIXED).get(0);
this.nbFixedPeriod = fixedLeg.getPaymentPeriods().size();
this.eta = eta;
SabrInterestRateParameters params = swaptionVolatilities.getParameters();
SabrFormulaData sabrPoint = SabrFormulaData.of(params.alpha(timeToExpiry, tenor),
params.beta(timeToExpiry, tenor), params.rho(timeToExpiry, tenor), params.nu(timeToExpiry, tenor));
this.shift = params.shift(timeToExpiry, tenor);
this.sabrExtrapolation = SabrExtrapolationRightFunction
.of(forward + shift, timeToExpiry, sabrPoint, cutOffStrike + shift, mu);
this.strike = strike;
this.factor = g(forward) / h(forward);
}
/**
* Obtains the integrant used in price replication.
*
* @return the integrant
*/
Function<Double, Double> integrant(){
return new Function<Double, Double>() {
@Override
public Double apply(Double x) {
double[] kD = kpkpp(x);
// Implementation note: kD[0] contains the first derivative of k; kD[1] the second derivative of k.
return factor * (kD[1] * (x - strike) + 2d * kD[0]) * bs(x);
}
};
}
/**
* The approximation of the discount factor as function of the swap rate.
*
* @param x the swap rate.
* @return the discount factor.
*/
double h(double x) {
return Math.pow(1d + x, eta);
}
/**
* The cash annuity.
*
* @param x the swap rate.
* @return the annuity.
*/
double g(double x) {
double periodFactor = 1d + x;
double nPeriodDiscount = Math.pow(periodFactor, -nbFixedPeriod);
return (1d - nPeriodDiscount) / x;
}
/**
* The factor used in the strike part and in the integration of the replication.
*
* @param x the swap rate.
* @return the factor.
*/
double k(double x) {
double g;
double h;
double periodFactor = 1d + x;
double nPeriodDiscount = Math.pow(periodFactor, -nbFixedPeriod);
g = (1d - nPeriodDiscount) / x;
h = Math.pow(1.0 + x, eta);
return h / g;
}
/**
* The first and second derivative of the function k.
* <p>
* The first element is the first derivative and the second element is second derivative.
*
* @param x the swap rate.
* @return the derivatives
*/
protected double[] kpkpp(double x) {
double periodFactor = 1d + x;
double nPeriodDiscount = Math.pow(periodFactor, -nbFixedPeriod);
/*The value of the annuity and its first and second derivative. */
double g, gp, gpp;
g = (1d - nPeriodDiscount) / x;
gp = -g / x + nbFixedPeriod * nPeriodDiscount / (x * periodFactor);
gpp = 2d / (x * x) * g - 2d * nbFixedPeriod * nPeriodDiscount / (x * x * periodFactor)
- (nbFixedPeriod + 1d) * nbFixedPeriod * nPeriodDiscount
/ (x * periodFactor * periodFactor);
double h = Math.pow(1d + x, eta);
double hp = eta * h / periodFactor;
double hpp = (eta - 1d) * hp / periodFactor;
double kp = hp / g - h * gp / (g * g);
double kpp = hpp / g - 2d * hp * gp / (g * g) - h * (gpp / (g * g) - 2d * (gp * gp) / (g * g * g));
return new double[] {kp, kpp };
}
/**
* The Black price with numeraire 1 as function of the strike.
*
* @param strike the strike.
* @return the Black price.
*/
double bs(double strike) {
return sabrExtrapolation.price(strike + shift, PutCall.CALL);
}
}
}