/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.volatility.surface; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve; import com.opengamma.analytics.financial.model.volatility.smile.function.SABRHaganVolatilityFunction; import com.opengamma.analytics.math.FunctionUtils; import com.opengamma.analytics.math.function.Function; import com.opengamma.analytics.math.surface.FunctionalDoublesSurface; import com.opengamma.util.test.TestGroup; /** * Test. */ @Test(groups = TestGroup.UNIT) public class BlackVolatilitySurfaceConverterTest { private static final BlackVolatilitySurfaceDelta DELTA_SURFACE; private static final BlackVolatilitySurfaceStrike STRIKE_SURFACE; private static final ForwardCurve FORWARD_CURVE; private static final double SPOT = 167; private static final double DRIFT = 0.03; static { FORWARD_CURVE = new ForwardCurve(SPOT, DRIFT); Function<Double, Double> func = new Function<Double, Double>() { @Override public Double evaluate(Double... td) { double delta = td[1]; return 0.2 + 2.0 * FunctionUtils.square(delta - 0.4); } }; DELTA_SURFACE = new BlackVolatilitySurfaceDelta(FunctionalDoublesSurface.from(func), FORWARD_CURVE); Function<Double, Double> func2 = new Function<Double, Double>() { final SABRHaganVolatilityFunction sabr = new SABRHaganVolatilityFunction(); @Override public Double evaluate(Double... tk) { double t = tk[0]; double k = tk[1]; double alpha = 0.3 * Math.exp(-0.3 * t) + 0.2; double beta = 1.0; double rho = -0.4; double nu = 0.6; return sabr.getVolatility(FORWARD_CURVE.getForward(t), k, t, alpha, beta, rho, nu); } }; STRIKE_SURFACE = new BlackVolatilitySurfaceStrike(FunctionalDoublesSurface.from(func2)); } @Test public void testValues() { for (int t = 0; t < 10; t++) { assertEquals(0.2, DELTA_SURFACE.getVolatilityForDelta(t, 0.4), 1e-6); } } @Test public void roundTripTest() { deltaToStrikeToDelta(DELTA_SURFACE); } private void deltaToStrikeToDelta(final BlackVolatilitySurfaceDelta originalDeltaSurface) { final BlackVolatilitySurfaceStrike strikeSurface = BlackVolatilitySurfaceConverter.toStrikeSurface(originalDeltaSurface); BlackVolatilitySurfaceDelta newDeltaSurface = BlackVolatilitySurfaceConverter.toDeltaSurface(strikeSurface, originalDeltaSurface.getForwardCurve()); for (int i = 0; i < 10; i++) { double t = Math.exp(i / 4.0) - 0.95; for (int j = 0; j < 21; j++) { double delta = 0.01 + 0.98 * j / 20.; double vol1 = originalDeltaSurface.getVolatilityForDelta(t, delta); double vol2 = newDeltaSurface.getVolatilityForDelta(t, delta); //System.out.println(t+"\t"+delta+"\t"+originalDeltaSurface.getVolatilityForDelta(t, delta)); assertEquals(vol1, vol2, 1e-9); } } } /** * The strike surface is from the Hagan SABR formula which is well known to exhibit arbitrage (that is negative prices of butterflies or equivalently a negative implied density * at some strike) for extreme strikes (the problem gets worse at time-to-expiry is increased).<p> * The round trip of strike to delta to strike surface CANNOT work at a strike where there is an arbitrage in the original surface. */ @Test public void roundTripTest2() { BlackVolatilitySurfaceDelta deltaSurface = BlackVolatilitySurfaceConverter.toDeltaSurface(STRIKE_SURFACE, FORWARD_CURVE); BlackVolatilitySurfaceStrike strikeSurface = BlackVolatilitySurfaceConverter.toStrikeSurface(deltaSurface); // double k = 1000; // double vol = STRIKE_SURFACE.getVolatility(t, k); // double delta = BlackFormulaRepository.delta(f, k, t, vol, true); // System.out.println(vol + "\t" + delta); // double vol2 = deltaSurface.getVolatilityForDelta(t, delta); // System.out.println("vol from delta surface " + vol2); // double k2 = BlackVolatilitySurfaceConverter.strikeForDelta(delta, STRIKE_SURFACE, FORWARD_CURVE, t); // System.out.println("strike for delta " + k2); // double k3 = BlackVolatilitySurfaceConverter.strikeForDelta(1e-4, STRIKE_SURFACE, FORWARD_CURVE, t); // System.out.println("strike for delta (debug)" + k3); // double delta2 = BlackVolatilitySurfaceConverter.deltaForStrike(k, deltaSurface, FORWARD_CURVE, t); // System.out.println("delta for strike " + delta2); // double expiry = 8.53; // double fwd = FORWARD_CURVE.getForward(expiry); // // for (int i = 0; i < 101; i++) { // double x = 0.0 + 5.0 * i / 100.; // double k = fwd * Math.exp(x); // double vol = STRIKE_SURFACE.getVolatility(expiry, k); // // double d = BlackVolatilitySurfaceConverter.deltaForStrike(k, deltaSurface, expiry); // double vol2 = deltaSurface.getVolatilityForDelta(expiry, d); // double price = BlackFormulaRepository.price(fwd, k, expiry, vol, true); // System.out.println(x + "\t" + k + "\t" + vol + "\t" + d + "\t" + vol2 + "\t" + price); // } // // System.out.println(); // for (int i = 0; i < 101; i++) { // double d = 0.001 + 0.998 * i / 100.; // // double vol = deltaSurface.getVolatilityForDelta(t, d); // double k = BlackVolatilitySurfaceConverter.strikeForDelta(d, STRIKE_SURFACE, FORWARD_CURVE, t); // double vol2 = STRIKE_SURFACE.getVolatility(t, k); // System.out.println(d + "\t" + vol + "\t" + k + "\t" + vol2); // // } // // double vol = strikeSurface.getVolatility(1.7682818284590456, 823.6246577301929); // // double debug = strikeSurface.getVolatility(6.44, 3.7); for (int i = 0; i < 10; i++) { double t = Math.exp(i / 4.0) - 0.95; double rootT = Math.sqrt(t); // double fwd = FORWARD_CURVE.getForward(t); for (int j = 0; j < 10; j++) { double k = SPOT * Math.exp(0.3 * rootT * (-4.5 + 6.0 * j / 9.)); double vol1 = STRIKE_SURFACE.getVolatility(t, k); double vol2 = strikeSurface.getVolatility(t, k); // System.out.println(t + "\t" + k + "\t" + vol1 + "\t" + vol2); // double delta1 = BlackFormulaRepository.delta(fwd, k, t, vol1, true); // double delta2 = BlackVolatilitySurfaceConverter.deltaForStrike(k, deltaSurface, t); // double k2 = BlackVolatilitySurfaceConverter.strikeForDelta(delta1, STRIKE_SURFACE, FORWARD_CURVE, t); // // System.out.println(delta1 + "\t" + delta2 + "\t" + k2); assertEquals(vol1, vol2, 1e-12); } } } }