/**
* Copyright (C) 2009 - 2011 by OpenGamma Inc.
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility.local;
import static org.testng.AssertJUnit.assertEquals;
import org.testng.annotations.Test;
import com.opengamma.analytics.financial.model.finitedifference.BoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDE1DCoefficients;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDESolver;
import com.opengamma.analytics.financial.model.finitedifference.DirichletBoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.ExponentialMeshing;
import com.opengamma.analytics.financial.model.finitedifference.HyperbolicMeshing;
import com.opengamma.analytics.financial.model.finitedifference.MeshingFunction;
import com.opengamma.analytics.financial.model.finitedifference.NeumannBoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.PDE1DDataBundle;
import com.opengamma.analytics.financial.model.finitedifference.PDEGrid1D;
import com.opengamma.analytics.financial.model.finitedifference.PDEResults1D;
import com.opengamma.analytics.financial.model.finitedifference.ThetaMethodFiniteDifference;
import com.opengamma.analytics.financial.model.finitedifference.applications.InitialConditionsProvider;
import com.opengamma.analytics.financial.model.finitedifference.applications.PDE1DCoefficientsProvider;
import com.opengamma.analytics.financial.model.finitedifference.applications.PDEUtilityTools;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRFormulaData;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRHaganVolatilityFunction;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceConverter;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceMoneyness;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceStrike;
import com.opengamma.analytics.financial.model.volatility.surface.PriceSurface;
import com.opengamma.analytics.math.function.Function;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolator;
import com.opengamma.analytics.math.interpolation.DoubleQuadraticInterpolator1D;
import com.opengamma.analytics.math.interpolation.FlatExtrapolator1D;
import com.opengamma.analytics.math.interpolation.GridInterpolator2D;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDoubleQuadraticDataBundle;
import com.opengamma.analytics.math.surface.FunctionalDoublesSurface;
/**
*
*/
public class DupireLocalVolatilityTest {
private static final DoubleQuadraticInterpolator1D INTERPOLATOR_1D = new DoubleQuadraticInterpolator1D();
private static final CombinedInterpolatorExtrapolator EXTRAPOLATOR_1D = new CombinedInterpolatorExtrapolator(INTERPOLATOR_1D, new FlatExtrapolator1D());
@SuppressWarnings("unused")
private static final GridInterpolator2D GRID_INTERPOLATOR2D = new GridInterpolator2D(EXTRAPOLATOR_1D, EXTRAPOLATOR_1D);
private static final DupireLocalVolatilityCalculator DUPIRE = new DupireLocalVolatilityCalculator();
private static final SABRHaganVolatilityFunction SABR = new SABRHaganVolatilityFunction();
private static final double SPOT = 0.04;
private static final double STRIKE = 0.05;
private static final double EXPIRY = 5.0;
private static final double ATM_VOL = 0.2;
private static final double ALPHA;
private static final double BETA = 0.5;
private static final double RHO = -0.2;
private static final double NU = 0.3;
// private static final SABRFormulaData SABR_DATA;
private static final double RATE = 0.05; //turn back to 5%
private static final double YIELD = 0.02;
private static final ForwardCurve FORWARD_CURVE = new ForwardCurve(SPOT, RATE - YIELD);
private static final PriceSurface PRICE_SURFACE;
private static final BlackVolatilitySurfaceStrike SABR_SURFACE;
private static AbsoluteLocalVolatilitySurface ABS_LOCAL_VOL;
private static LocalVolatilitySurfaceStrike LOCAL_VOL;
/**
*
*/
static {
ALPHA = ATM_VOL * Math.pow(SPOT, 1 - BETA);
// SABR_DATA = new SABRFormulaData(ALPHA, BETA, RHO, NU);
final Function<Double, Double> sabrSurface = new Function<Double, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double... x) {
final double t = x[0];
final double k = x[1];
final SABRFormulaData sabrdata = new SABRFormulaData(ALPHA, BETA, RHO, NU);
final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true);
final Function1D<SABRFormulaData, Double> func = SABR.getVolatilityFunction(option, FORWARD_CURVE.getForward(t));
return func.evaluate(sabrdata);
}
};
SABR_SURFACE = new BlackVolatilitySurfaceStrike(FunctionalDoublesSurface.from(sabrSurface));
final Function<Double, Double> priceSurface = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... tk) {
final double t = tk[0];
final double k = tk[1];
final double sigma = sabrSurface.evaluate(tk);
final double df = Math.exp(-RATE * t);
@SuppressWarnings("synthetic-access")
final double price = BlackFormulaRepository.price(FORWARD_CURVE.getForward(t), k, t, sigma, true);
if (Double.isNaN(price)) {
System.out.println("Error");
}
return price * df;
}
};
PRICE_SURFACE = new PriceSurface(FunctionalDoublesSurface.from(priceSurface));
LOCAL_VOL = DUPIRE.getLocalVolatility(SABR_SURFACE, FORWARD_CURVE);
ABS_LOCAL_VOL = DUPIRE.getAbsoluteLocalVolatilitySurface(SABR_SURFACE, SPOT, RATE);
}
@Test
public void testImpliedVolCal() {
final LocalVolatilitySurfaceStrike lv = DUPIRE.getLocalVolatility(PRICE_SURFACE, SPOT, RATE, YIELD);
final double vol1 = lv.getVolatility(EXPIRY, STRIKE);
final double vol2 = LOCAL_VOL.getVolatility(EXPIRY, STRIKE);
assertEquals(vol1, vol2, 1e-6);
}
@Test
public void testImpliedVolMoneynessCal() {
final LocalVolatilitySurfaceStrike lv = DUPIRE.getLocalVolatility(PRICE_SURFACE, SPOT, RATE, YIELD);
final double vol1 = lv.getVolatility(EXPIRY, STRIKE);
final BlackVolatilitySurfaceMoneyness miv = BlackVolatilitySurfaceConverter.toMoneynessSurface(SABR_SURFACE, FORWARD_CURVE);
final LocalVolatilitySurfaceMoneyness lvm = DUPIRE.getLocalVolatility(miv);
final double vol2 = lvm.getVolatility(EXPIRY, STRIKE);
assertEquals(vol1, vol2, 1e-6);
}
@Test(enabled = false)
public void printSurfaces() {
final SABRHaganVolatilityFunction sabr = new SABRHaganVolatilityFunction();
final double k = 0.01;
for (int i = 0; i < 10; i++) {
final double t = 0.5 + i * 20 / 9.;
final double vol1 = SABR_SURFACE.getVolatility(t, k);
final double vol2 = sabr.getVolatility(SPOT, k, t, ALPHA, BETA, RHO, NU);
System.out.println(t + "\t" + vol1 + "\t" + vol2);
}
PDEUtilityTools.printSurface("Imp Vol", SABR_SURFACE.getSurface(), 0., 5., 0.1 * SPOT, 3 * SPOT);
PDEUtilityTools.printSurface("Loc Vol", LOCAL_VOL.getSurface(), 0., 5., 0.1 * SPOT, 3 * SPOT);
PDEUtilityTools.printSurface("ABs Loc Vol", ABS_LOCAL_VOL.getSurface(), 0., 5., 0.1 * SPOT, 3 * SPOT);
}
@Test
public void pdePriceTest() {
final PDE1DCoefficientsProvider pde_provider = new PDE1DCoefficientsProvider();
final InitialConditionsProvider int_provider = new InitialConditionsProvider();
//final ZZConvectionDiffusionPDEDataBundle db = provider.getBackwardsLocalVol(STRIKE, EXPIRY, true, LOCAL_VOL, FORWARD_CURVE);
final ConvectionDiffusionPDE1DCoefficients pde = pde_provider.getBackwardsLocalVol(FORWARD_CURVE, EXPIRY, LOCAL_VOL);
final Function1D<Double, Double> payoff = int_provider.getEuropeanPayoff(STRIKE, true);
final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(0.5, false);
final double forward = FORWARD_CURVE.getForward(EXPIRY);
final int nTimeNodes = 50;
final int nSpotNodes = 100;
final double upperLevel = 3.5 * forward;
final BoundaryCondition lower = new DirichletBoundaryCondition(0, 0);
final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, upperLevel, false);
final MeshingFunction timeMesh = new ExponentialMeshing(0.0, EXPIRY, nTimeNodes, 6.0);
final MeshingFunction spaceMesh = new HyperbolicMeshing(0, upperLevel, STRIKE, nSpotNodes, 0.05);
final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh);
final PDEResults1D res = solver.solve(new PDE1DDataBundle<>(pde, payoff, lower, upper, grid));
final int fwdIndex = grid.getLowerBoundIndexForSpace(forward);
final double[] fwd = new double[4];
final double[] vol = new double[4];
for (int i = 0; i < 4; i++) {
fwd[i] = grid.getSpaceNode(i + fwdIndex - 1);
final double price = res.getFunctionValue(i + fwdIndex - 1);
vol[i] = BlackFormulaRepository.impliedVolatility(price, fwd[i], STRIKE, EXPIRY, true);
}
final Interpolator1DDoubleQuadraticDataBundle idb = INTERPOLATOR_1D.getDataBundle(fwd, vol);
final double sabrVol = SABR_SURFACE.getVolatility(EXPIRY, STRIKE);
final double modelVol = INTERPOLATOR_1D.interpolate(idb, forward);
assertEquals("Volatility test", sabrVol, modelVol, 1e-4); //1bps error
}
@Test(enabled = false)
public void printPriceTest() {
double t;
double k;
double price;
for (int j = 0; j < 101; j++) {
t = 0.01 + 5.0 * j / 100.0;
System.out.print("\t" + t);
}
System.out.print("\n");
for (int i = 0; i < 101; i++) {
k = 0.001 + 0.15 * i / 100.0;
System.out.print(k);
for (int j = 0; j < 101; j++) {
t = 0.01 + 5.0 * j / 100.0;
price = PRICE_SURFACE.getPrice(t, k);
System.out.print("\t" + price);
}
System.out.print("\n");
}
}
@Test(enabled = false)
public void priceTest() {
final DupireLocalVolatilityCalculator cal = new DupireLocalVolatilityCalculator();
final LocalVolatilitySurfaceStrike locVol = cal.getLocalVolatility(PRICE_SURFACE, SPOT, RATE, 0.0);
double t;
double f;
double vol;
for (int j = 0; j < 101; j++) {
t = 0.01 + 5.0 * j / 100.0;
System.out.print("\t" + t);
}
System.out.print("\n");
for (int i = 0; i < 101; i++) {
f = 0.001 + 0.15 * i / 100.0;
System.out.print(f);
for (int j = 0; j < 101; j++) {
t = 0.01 + 5.0 * j / 100.0;
vol = locVol.getVolatility(t, f);
System.out.print("\t" + vol);
}
System.out.print("\n");
}
}
@SuppressWarnings("deprecation")
@Test(enabled = false)
public void volTest() {
final DupireLocalVolatilityCalculator cal = new DupireLocalVolatilityCalculator();
final LocalVolatilitySurfaceStrike locVol = cal.getLocalVolatility(SABR_SURFACE, SPOT, RATE);
double t;
double f;
double vol;
for (int j = 0; j < 101; j++) {
t = 0.01 + 5.0 * j / 100.0;
System.out.print("\t" + t);
}
System.out.print("\n");
for (int i = 0; i < 101; i++) {
f = 0.001 + 0.15 * i / 100.0;
System.out.print(f);
for (int j = 0; j < 101; j++) {
t = 0.01 + 5.0 * j / 100.0;
vol = locVol.getVolatility(t, f);
System.out.print("\t" + vol);
}
System.out.print("\n");
}
}
}