/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.option; import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import com.opengamma.strata.basics.value.ValueDerivatives; import com.opengamma.strata.product.common.PutCall; /** * Test {@link NormalFormulaRepository} implied volatility. */ @Test public class NormalFormulaRepositoryImpliedVolatilityTest { private static final double FORWARD = 100.0; private static final double DF = 0.87; private static final double T = 4.5; private static final NormalFunctionData[] DATA; private static final int N = 10; private static final double[] PRICES; private static final double[] STRIKES = new double[N]; private static final double[] STRIKES_ATM = new double[N]; private static final EuropeanVanillaOption[] OPTIONS = new EuropeanVanillaOption[N]; private static final double[] SIGMA; private static final double[] SIGMA_BLACK = new double[N]; private static final NormalPriceFunction FUNCTION = new NormalPriceFunction(); static { PRICES = new double[N]; SIGMA = new double[N]; DATA = new NormalFunctionData[N]; for (int i = 0; i < N; i++) { STRIKES[i] = FORWARD + (-N / 2 + i) * 10; STRIKES_ATM[i] = FORWARD + (-0.5d * N + i) / 100.0d; SIGMA[i] = FORWARD * (0.05 + 4.0 * i / 100.0); SIGMA_BLACK[i] = 0.20 + i / 100.0d; DATA[i] = NormalFunctionData.of(FORWARD, DF, SIGMA[i]); OPTIONS[i] = EuropeanVanillaOption.of(STRIKES[i], T, PutCall.CALL); PRICES[i] = FUNCTION.getPriceFunction(OPTIONS[i]).apply(DATA[i]); } } private static final double TOLERANCE_PRICE = 1.0E-4; private static final double TOLERANCE_VOL = 1.0E-6; public void implied_volatility() { double[] impliedVolatility = new double[N]; for (int i = 0; i < N; i++) { impliedVolatility[i] = impliedVolatility(DATA[i], OPTIONS[i], PRICES[i]); assertEquals(SIGMA[i], impliedVolatility[i], 1e-6); } } public void intrinsic_price() { NormalFunctionData data = NormalFunctionData.of(1.0, 1.0, 0.01); EuropeanVanillaOption option1 = EuropeanVanillaOption.of(0.5, 1.0, PutCall.CALL); assertThrowsIllegalArg(() -> impliedVolatility(data, option1, 1e-6)); EuropeanVanillaOption option2 = EuropeanVanillaOption.of(1.5, 1.0, PutCall.PUT); assertThrowsIllegalArg(() -> impliedVolatility(data, option2, 1e-6)); } private double impliedVolatility( NormalFunctionData data, EuropeanVanillaOption option, double price) { return NormalFormulaRepository.impliedVolatility( price, data.getForward(), option.getStrike(), option.getTimeToExpiry(), data.getNormalVolatility(), data.getNumeraire(), option.getPutCall()); } @Test(expectedExceptions = IllegalArgumentException.class) public void wrong_strike() { NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, -1.0d, T, 0.20d); } @Test(expectedExceptions = IllegalArgumentException.class) public void wrong_forward() { NormalFormulaRepository.impliedVolatilityFromBlackApproximated(-1.0d, FORWARD, T, 0.20d); } public void price_comparison() { priceCheck(STRIKES); priceCheck(STRIKES_ATM); } private void priceCheck(double[] strikes) { for (int i = 0; i < N; i++) { double ivNormalComputed = NormalFormulaRepository .impliedVolatilityFromBlackApproximated(FORWARD, strikes[i], T, SIGMA_BLACK[i]); double priceNormalComputed = NormalFormulaRepository.price(FORWARD, strikes[i], T, ivNormalComputed, PutCall.CALL) * DF; double priceBlack = BlackFormulaRepository.price(FORWARD, strikes[i], T, SIGMA_BLACK[i], true) * DF; assertEquals(priceBlack, priceNormalComputed, TOLERANCE_PRICE); } } public void implied_volatility_adjoint() { double shiftFd = 1.0E-6; for (int i = 0; i < N; i++) { double impliedVol = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, STRIKES[i], T, SIGMA_BLACK[i]); ValueDerivatives impliedVolAdj = NormalFormulaRepository.impliedVolatilityFromBlackApproximatedAdjoint(FORWARD, STRIKES[i], T, SIGMA_BLACK[i]); assertEquals(impliedVol, impliedVolAdj.getValue(), TOLERANCE_VOL); double impliedVolP = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, STRIKES[i], T, SIGMA_BLACK[i] + shiftFd); double impliedVolM = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, STRIKES[i], T, SIGMA_BLACK[i] - shiftFd); double derivativeApproximated = (impliedVolP - impliedVolM) / (2 * shiftFd); assertEquals(1, impliedVolAdj.getDerivatives().size()); assertEquals(derivativeApproximated, impliedVolAdj.getDerivative(0), TOLERANCE_VOL); } } }