/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility;
import static org.testng.AssertJUnit.assertEquals;
import org.testng.annotations.Test;
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.financial.model.option.pricing.analytic.formula.NormalFunctionData;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.NormalPriceFunction;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRFormulaData;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRHaganVolatilityFunction;
import com.opengamma.util.test.TestGroup;
/**
* Test {@link BlackImpliedVolatilityFormula}.
*/
@Test(groups = TestGroup.UNIT)
public class BlackImpliedVolatilityFormulaTest {
private static final double FORWARD = 134.5;
private static final double DF = 0.87;
private static final double T = 4.5;
private static final double SIGMA = 0.2;
private static final int N = 10;
private static final BlackFunctionData[] DATA = new BlackFunctionData[N];
private static final EuropeanVanillaOption[] OPTIONS = new EuropeanVanillaOption[N];
private static final double[] SIGMA_NORMAL = new double[N];
private static final double[] PRICES = new double[N];
private static final double[] STRIKES = new double[N];
private static final double[] STRIKES_ATM = new double[N];
private static final BlackPriceFunction FORMULA = new BlackPriceFunction();
private static final NormalPriceFunction FUNCTION_PRICE_NORMAL = new NormalPriceFunction();
private static final SABRHaganVolatilityFunction SABR = new SABRHaganVolatilityFunction();
private static final SABRFormulaData SABR_DATA;
private static final double TOLERANCE_PRICE = 1.0E-4;
static {
for (int i = 0; i < 10; i++) {
STRIKES[i] = FORWARD - 20 + 40 / N * i;
STRIKES_ATM[i] = FORWARD + (-0.5d * N + i) / 100.0d;
DATA[i] = new BlackFunctionData(FORWARD, DF, SIGMA);
OPTIONS[i] = new EuropeanVanillaOption(STRIKES[i], T, true);
PRICES[i] = FORMULA.getPriceFunction(OPTIONS[i]).evaluate(DATA[i]);
SIGMA_NORMAL[i] = 15.0 + i / 10.0d;
}
double beta = 0.6;
double alpha = Math.pow(SIGMA, 1 - beta);
double rho = -0.3;
double nu = 0.4;
SABR_DATA = new SABRFormulaData(alpha, beta, rho, nu);
}
@Test
public void test() {
final BlackImpliedVolatilityFormula formula = new BlackImpliedVolatilityFormula();
for (int i = 0; i < N; i++) {
final double vol = formula.getImpliedVolatility(DATA[i], OPTIONS[i], PRICES[i]);
assertEquals(SIGMA, vol, 1e-6);
}
}
@Test
public void flatTest() {
final double rootT = Math.sqrt(T);
for (int i = 0; i < 51; i++) {
double d = -5 + 12.0 * i / 50.;
double k = FORWARD * Math.exp(d * rootT);
boolean isCall = k >= FORWARD;
double price = BlackFormulaRepository.price(FORWARD, k, T, SIGMA, isCall);
double impVol = BlackFormulaRepository.impliedVolatility(price, FORWARD, k, T, isCall);
assertEquals(SIGMA, impVol, 1e-6);
}
}
@Test
public void sabrTest() {
final double rootT = Math.sqrt(T);
//this has a lowest price of 4e-18
for (int i = 0; i < 51; i++) {
double d = -9.0 + 12.0 * i / 50.;
double k = FORWARD * Math.exp(d * rootT);
boolean isCall = k >= FORWARD;
double vol = SABR.getVolatility(new EuropeanVanillaOption(k, T, true), FORWARD, SABR_DATA);
double price = BlackFormulaRepository.price(FORWARD, k, T, vol, isCall);
double impVol = BlackFormulaRepository.impliedVolatility(price, FORWARD, k, T, isCall);
// System.out.println(k + "\t" + price + "\t" + vol + "\t" + impVol);
assertEquals(vol, impVol, 1e-8);
}
//this has a lowest price of 1e-186
for (int i = 0; i < 21; i++) {
double d = 3.0 + 4.0 * i / 20.;
double k = FORWARD * Math.exp(d * rootT);
boolean isCall = k >= FORWARD;
double vol = SABR.getVolatility(new EuropeanVanillaOption(k, T, true), FORWARD, SABR_DATA);
double price = BlackFormulaRepository.price(FORWARD, k, T, vol, isCall);
double impVol = BlackFormulaRepository.impliedVolatility(price, FORWARD, k, T, isCall);
// System.out.println(k + "\t" + price + "\t" + vol + "\t" + impVol);
assertEquals(vol, impVol, 1e-3);
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void wrong_strike() {
BlackImpliedVolatilityFormula.impliedVolatilityFromNormalApproximated(FORWARD, -1.0d, T, 0.20d);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void wrong_forward() {
BlackImpliedVolatilityFormula.impliedVolatilityFromNormalApproximated(-1.0d, FORWARD, T, 0.20d);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void wrong_strike2() {
BlackImpliedVolatilityFormula.impliedVolatilityFromNormalApproximated2(FORWARD, -1.0d, T, 0.20d);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void wrong_forward2() {
BlackImpliedVolatilityFormula.impliedVolatilityFromNormalApproximated2(-1.0d, FORWARD, T, 0.20d);
}
@Test
public void price_comparison_normal() {
priceCheck(STRIKES);
priceCheck(STRIKES_ATM);
}
private void priceCheck(double[] strikes) {
for (int i = 5; i < N; i++) {
double ivBlackComputed = BlackImpliedVolatilityFormula
.impliedVolatilityFromNormalApproximated(FORWARD, strikes[i], T, SIGMA_NORMAL[i]);
EuropeanVanillaOption o = new EuropeanVanillaOption(strikes[i], T, true);
NormalFunctionData d = new NormalFunctionData(FORWARD, DF, SIGMA_NORMAL[i]);
double priceBlackComputed = BlackFormulaRepository.price(FORWARD, strikes[i], T, ivBlackComputed, true) * DF;
double priceNormal = FUNCTION_PRICE_NORMAL.getPriceFunction(o).evaluate(d);
assertEquals("check " + i,
priceNormal, priceBlackComputed, TOLERANCE_PRICE);
}
}
}