/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.future.provider;
import static org.testng.AssertJUnit.assertEquals;
import org.testng.annotations.Test;
import com.opengamma.analytics.financial.interestrate.future.derivative.BondFuturesOptionMarginSecurity;
import com.opengamma.analytics.financial.legalentity.LegalEntity;
import com.opengamma.analytics.financial.model.volatility.smile.function.SSVIVolatilityFunction;
import com.opengamma.analytics.financial.provider.description.IssuerProviderDiscountDataSets;
import com.opengamma.analytics.financial.provider.description.interestrate.BlackBondFuturesFlatProvider;
import com.opengamma.analytics.financial.provider.description.interestrate.BlackBondFuturesSsviExpiryPriceProvider;
import com.opengamma.analytics.financial.provider.description.interestrate.BlackBondFuturesSsviConstantPriceProvider;
import com.opengamma.analytics.financial.provider.description.interestrate.BlackBondFuturesSsviPriceProvider;
import com.opengamma.analytics.financial.provider.description.interestrate.IssuerProviderDiscount;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MulticurveSensitivity;
import com.opengamma.analytics.financial.util.AssertSensitivityObjects;
import com.opengamma.analytics.math.curve.DoublesCurve;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.analytics.math.differentiation.FiniteDifferenceType;
import com.opengamma.analytics.math.differentiation.ScalarFieldFirstOrderDifferentiator;
import com.opengamma.analytics.math.differentiation.ValueDerivatives;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.interpolation.Interpolator1DFactory;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.surface.ConstantDoublesSurface;
import com.opengamma.analytics.math.surface.Surface;
import com.opengamma.util.test.TestGroup;
/**
* Tests {@link BondFutureOptionMarginSecurityBlackSsviPriceMethod}.
*/
@Test(groups = TestGroup.UNIT)
public class BondFutureOptionMarginSecurityBlackSsviPriceMethodTest {
/** Curves for a specific issuer name */
private static final IssuerProviderDiscount ISSUER_SPECIFIC_MULTICURVES = IssuerProviderDiscountDataSets.getIssuerSpecificProvider();
/** The legal entity */
private static final LegalEntity[] LEGAL_ENTITIES = IssuerProviderDiscountDataSets.getIssuers();
private static final LegalEntity LEGAL_ENTITY_GERMANY = LEGAL_ENTITIES[2];
private static final BondFuturesOptionMarginSecurity CALL_BOBL_116 =
BondFuturesOptionMarginSecurityBlackExpLogMoneynessMethodTest.CALL_BOBL_116;
private static final BondFuturesOptionMarginSecurity PUT_BOBL_116 =
BondFuturesOptionMarginSecurityBlackExpLogMoneynessMethodTest.PUT_BOBL_116;
/* Methods */
private static final BondFutureOptionMarginSecurityBlackSsviPriceMethod METHOD_SSVI =
BondFutureOptionMarginSecurityBlackSsviPriceMethod.DEFAULT;
private static final BondFutureOptionMarginSecurityBlackPriceMethod METHOD_BLACK =
BondFutureOptionMarginSecurityBlackPriceMethod.getInstance();
private static final ScalarFieldFirstOrderDifferentiator DIFFERENTIATOR =
new ScalarFieldFirstOrderDifferentiator(FiniteDifferenceType.CENTRAL, 1.0E-5);
/* SSVI data */
private static final Interpolator1D LINEAR_FLAT = CombinedInterpolatorExtrapolatorFactory
.getInterpolator(Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR,
Interpolator1DFactory.FLAT_EXTRAPOLATOR);
private static final double RHO = 0.25;
private static final double ETA = 0.50;
private static final DoublesCurve VOLATILITY_ATM;
static{
double[] times = {0.0, 0.5, 1.0, 5.0};
double[] vol = {0.01, 0.011, 0.012, 0.01};
VOLATILITY_ATM = new InterpolatedDoublesCurve(times, vol, LINEAR_FLAT, true);
}
private static final DoublesCurve RHO_EXPIRY;
private static final DoublesCurve ETA_EXPIRY;
static{
double[] times = {0.0, 0.5, 1.0, 5.0};
double[] rho = {0.25, 0.26, 0.27, 0.28};
RHO_EXPIRY = new InterpolatedDoublesCurve(times, rho, LINEAR_FLAT, true);
double[] eta = {0.50, 0.48, 0.46, 0.44};
ETA_EXPIRY = new InterpolatedDoublesCurve(times, eta, LINEAR_FLAT, true);
}
private static final BlackBondFuturesSsviPriceProvider SSVI_PROVIDER =
new BlackBondFuturesSsviConstantPriceProvider(ISSUER_SPECIFIC_MULTICURVES, VOLATILITY_ATM, RHO, ETA, LEGAL_ENTITY_GERMANY);
private static final BlackBondFuturesSsviPriceProvider SSVI_PROVIDER_EXPIRY =
new BlackBondFuturesSsviExpiryPriceProvider(ISSUER_SPECIFIC_MULTICURVES, VOLATILITY_ATM, RHO_EXPIRY, ETA_EXPIRY, LEGAL_ENTITY_GERMANY);
/* Black equivalent */
private static final double STRIKE_PRICE = CALL_BOBL_116.getStrike();
private static final double FUTURES_PRICE =
METHOD_BLACK.getMethodFutures().price(CALL_BOBL_116.getUnderlyingFuture(), SSVI_PROVIDER);
private static final double TIME_EXP = CALL_BOBL_116.getExpirationTime();
private static final double BLACK_IV = SSVI_PROVIDER
.getVolatility(TIME_EXP, 0.0, STRIKE_PRICE, FUTURES_PRICE);
private static final Surface<Double, Double, Double> BLACK_SURFACE = new ConstantDoublesSurface(BLACK_IV);
private static final BlackBondFuturesFlatProvider BLACK_PROVIDER =
new BlackBondFuturesFlatProvider(ISSUER_SPECIFIC_MULTICURVES, BLACK_SURFACE, LEGAL_ENTITY_GERMANY);
private static final double BLACK_IV_EXPIRY = SSVI_PROVIDER_EXPIRY
.getVolatility(TIME_EXP, 0.0, STRIKE_PRICE, FUTURES_PRICE);
private static final Surface<Double, Double, Double> BLACK_SURFACE_EXPIRY = new ConstantDoublesSurface(BLACK_IV_EXPIRY);
private static final BlackBondFuturesFlatProvider BLACK_PROVIDER_EXPIRY =
new BlackBondFuturesFlatProvider(ISSUER_SPECIFIC_MULTICURVES, BLACK_SURFACE_EXPIRY, LEGAL_ENTITY_GERMANY);
private static final double TOLERANCE_PRICE = 1.0E-8;
private static final double TOLERANCE_PRICE_DELTA = 1.0E-6;
private static final double TOLERANCE_PRICE_DELTA_RELATIVE = 1.0E-3;
@Test
public void price() {
double priceBlack = METHOD_BLACK.price(CALL_BOBL_116, BLACK_PROVIDER);
double priceSsvi = METHOD_SSVI.price(CALL_BOBL_116, SSVI_PROVIDER);
assertEquals("SSVI formula: price", priceBlack, priceSsvi, TOLERANCE_PRICE);
}
@Test
public void price_expiry_rho_eta() {
double priceBlack = METHOD_BLACK.price(CALL_BOBL_116, BLACK_PROVIDER_EXPIRY);
double priceSsvi = METHOD_SSVI.price(CALL_BOBL_116, SSVI_PROVIDER_EXPIRY);
assertEquals("SSVI formula: price", priceBlack, priceSsvi, TOLERANCE_PRICE);
}
@Test
public void price_put_call_parity() {
double priceCall = METHOD_SSVI.price(CALL_BOBL_116, SSVI_PROVIDER);
double pricePut = METHOD_SSVI.price(PUT_BOBL_116, SSVI_PROVIDER);
double priceFuture = METHOD_SSVI.underlyingFuturePrice(CALL_BOBL_116, ISSUER_SPECIFIC_MULTICURVES);
assertEquals("SSVI formula: price", priceCall - pricePut, priceFuture - STRIKE_PRICE, TOLERANCE_PRICE);
}
@Test
public void implied_volatility() {
double ivSsvi = METHOD_SSVI.impliedVolatility(CALL_BOBL_116, SSVI_PROVIDER);
assertEquals("SSVI formula: implied volatility", ivSsvi, BLACK_IV, TOLERANCE_PRICE);
}
@Test
public void implied_volatility_expiry() {
double ivSsvi = METHOD_SSVI.impliedVolatility(CALL_BOBL_116, SSVI_PROVIDER_EXPIRY);
assertEquals("SSVI formula: implied volatility", ivSsvi, BLACK_IV_EXPIRY, TOLERANCE_PRICE);
}
@Test
public void price_curve_sensitivity() {
MulticurveSensitivity pcsBlack = METHOD_BLACK.priceCurveSensitivity(CALL_BOBL_116, BLACK_PROVIDER);
MulticurveSensitivity pcsSsvi = METHOD_SSVI.priceCurveSensitivity(CALL_BOBL_116, SSVI_PROVIDER);
AssertSensitivityObjects.assertEquals("SSVI formula: sensitivity", pcsBlack, pcsSsvi, TOLERANCE_PRICE_DELTA);
}
@Test
public void price_black_sensitivity() {
double vegaBlack = METHOD_BLACK.vega(CALL_BOBL_116, BLACK_PROVIDER);
double vegaSsvi = METHOD_SSVI.vega(CALL_BOBL_116, SSVI_PROVIDER);
assertEquals("SSVI formula: price Black sensitivity", vegaBlack, vegaSsvi, TOLERANCE_PRICE_DELTA);
}
@Test
public void price_ssvi_sensitivity() {
double vega = METHOD_SSVI.vega(CALL_BOBL_116, BLACK_PROVIDER);
ValueDerivatives ssviPriceSensitivity = METHOD_SSVI.priceSsviSensitivity(CALL_BOBL_116, SSVI_PROVIDER);
ValueDerivatives ssviVolSensitivity = SSVIVolatilityFunction
.volatilityAdjoint(FUTURES_PRICE, STRIKE_PRICE, TIME_EXP, VOLATILITY_ATM.getYValue(TIME_EXP), RHO, ETA);
for (int i = 0; i < 3; i++) {
assertEquals("SSVI formula: price SSVI parameters sensitivity",
ssviPriceSensitivity.getDerivatives(i), ssviVolSensitivity.getDerivatives(i+3) * vega,
TOLERANCE_PRICE_DELTA);
}
}
@Test
public void price_ssvi_sensitivity_expiry() {
double vega = METHOD_SSVI.vega(CALL_BOBL_116, BLACK_PROVIDER_EXPIRY);
ValueDerivatives ssviPriceSensitivity = METHOD_SSVI.priceSsviSensitivity(CALL_BOBL_116, SSVI_PROVIDER_EXPIRY);
ValueDerivatives ssviVolSensitivity = SSVIVolatilityFunction
.volatilityAdjoint(FUTURES_PRICE, STRIKE_PRICE, TIME_EXP,
VOLATILITY_ATM.getYValue(TIME_EXP), RHO_EXPIRY.getYValue(TIME_EXP), ETA_EXPIRY.getYValue(TIME_EXP));
for (int i = 0; i < 3; i++) {
assertEquals("SSVI formula: price SSVI parameters sensitivity",
ssviPriceSensitivity.getDerivatives(i), ssviVolSensitivity.getDerivatives(i+3) * vega,
TOLERANCE_PRICE_DELTA);
}
}
@Test
public void price_ssvi_sensitivity_fd() {
ValueDerivatives ssviPriceSensitivity = METHOD_SSVI.priceSsviSensitivity(CALL_BOBL_116, SSVI_PROVIDER);
Function1D<DoubleMatrix1D, Double> function = new Function1D<DoubleMatrix1D, Double>() {
private static final long serialVersionUID = 1L;
@Override
public Double evaluate(DoubleMatrix1D x) {
Double[] vol = VOLATILITY_ATM.getYData().clone();
for(int i=0; i<vol.length; i++){
vol[i] += x.getEntry(0);
}
DoublesCurve volatilityAtm = new InterpolatedDoublesCurve(VOLATILITY_ATM.getXData(), vol, LINEAR_FLAT, true);
BlackBondFuturesSsviConstantPriceProvider ssviProvider =
new BlackBondFuturesSsviConstantPriceProvider(ISSUER_SPECIFIC_MULTICURVES, volatilityAtm,
RHO + x.getEntry(1), ETA + x.getEntry(2), LEGAL_ENTITY_GERMANY);
return METHOD_SSVI.price(CALL_BOBL_116, ssviProvider);
}
};
Function1D<DoubleMatrix1D, DoubleMatrix1D> d = DIFFERENTIATOR.differentiate(function);
DoubleMatrix1D fd = d.evaluate(new DoubleMatrix1D(0.0, 0.0, 0.0));
for (int j = 0; j < 3; j++) {
assertEquals("SSVI formula: price SSVI parameters sensitivity",
fd.getEntry(j), ssviPriceSensitivity.getDerivatives(j), TOLERANCE_PRICE_DELTA);
assertEquals("SSVI formula: price SSVI parameters sensitivity",
(fd.getEntry(j) - ssviPriceSensitivity.getDerivatives(j)) / ssviPriceSensitivity.getDerivatives()[j], 0.0,
TOLERANCE_PRICE_DELTA_RELATIVE);
}
}
}