/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.option.pricing.analytic; import static org.testng.AssertJUnit.assertEquals; import java.util.Arrays; import org.testng.annotations.Test; import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository; import com.opengamma.util.test.TestGroup; /** * Test. */ @Test(groups = TestGroup.UNIT) public class BaroneAdesiWhaleyModelTest { private static final double STRIKE = 100; private static final double R = 0.1; private static final double B = 0.0; private static final double[] COM_PRICES = new double[] {90, 100, 110 }; private static final double[] VOLS = new double[] {0.15, 0.25, 0.35 }; private static final double[] EXPIRIES = new double[] {0.1, 0.5 }; private static final double[][][] CALL_PRICES = new double[][][] { { //taken from Haug p. 100 table 3.1 {0.0206, 1.8771, 10.0089 }, {0.3159, 3.1280, 10.3919 }, {0.9495, 4.3777, 11.1679 } }, { {0.8208, 4.0842, 10.8087 }, {2.7437, 6.8015, 13.0170 }, {5.0063, 9.5106, 15.5689 } } }; private static final double[][][] PUT_PRICES = new double[][][] { { {10.0, 1.8770, 0.0410 }, {10.2533, 3.1277, 0.4562 }, {10.8787, 4.3777, 1.2402 } }, { {10.5595, 4.0842, 1.0822 }, {12.4419, 6.8014, 3.3226 }, {14.6945, 9.5104, 5.8823 } } }; //TODO either the values in Haug are wrong, or we have a subtle bug @Test public void knownValuesTest() { final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); for (int i = 0; i < EXPIRIES.length; i++) { for (int j = 0; j < VOLS.length; j++) { for (int k = 0; k < COM_PRICES.length; k++) { final double call = baw.price(COM_PRICES[k], STRIKE, R, B, EXPIRIES[i], VOLS[j], true); final double put = baw.price(COM_PRICES[k], STRIKE, R, B, EXPIRIES[i], VOLS[j], false); assertEquals("call", CALL_PRICES[i][j][k], call, 3e-3); //these should be 1e-4 assertEquals("put", PUT_PRICES[i][j][k], put, 1e-3); } } } } @Test //(enabled = false) public void test() { final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); final double s0 = 110; final double k = 80; final double t = 0.5; final double r = 0.1; final double b = -0.04; final double sigma = 0.3; final boolean isCall = true; final double bawPrice = baw.price(s0, k, r, b, t, sigma, isCall); final double bsprice = Math.exp(-r * t) * BlackFormulaRepository.price(s0 * Math.exp(b * t), k, t, sigma, isCall); //TODO test me //System.out.println(bawPrice + " " + bsprice); final double impVol = baw.impliedVolatility(bawPrice, s0, k, r, b, t, isCall); //TODO test me //System.out.println(impVol); } @Test public void sCritSensitivityTest() { final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); final double s0 = 110; final double k = 100; final double t = 0.5; final double r = 0.1; final double b = -0.03; final double sigma = 0.35; final double eps = 1e-5; for (int i = 0; i < 2; i++) { final boolean isCall = i == 0; //delta final double sUp = baw.sCrit(s0 + eps, k, r, b, t, sigma, isCall); final double sDown = baw.sCrit(s0 - eps, k, r, b, t, sigma, isCall); final double fdDelta = (sUp - sDown) / 2 / eps; assertEquals("delta", 0.0, fdDelta, 0.0); //sCrit has no dependence on s0 final double[] sCAdj = baw.getsCritAdjoint(s0, k, r, b, t, sigma, isCall); //dual-delta final double kUp = baw.sCrit(s0, k + eps, r, b, t, sigma, isCall); final double kDown = baw.sCrit(s0, k - eps, r, b, t, sigma, isCall); final double fdDD = (kUp - kDown) / 2 / eps; assertEquals("dual-delta", sCAdj[0], fdDD, 1e-7); //rho final double rUp = baw.sCrit(s0, k, r + eps, b, t, sigma, isCall); final double rDown = baw.sCrit(s0, k, r - eps, b, t, sigma, isCall); final double fdRho = (rUp - rDown) / 2 / eps; assertEquals("rho", sCAdj[1], fdRho, 1e-5); //b-rho final double bUp = baw.sCrit(s0, k, r, b + eps, t, sigma, isCall); final double bDown = baw.sCrit(s0, k, r, b - eps, t, sigma, isCall); final double fdBRho = (bUp - bDown) / 2 / eps; assertEquals("b-rho", sCAdj[2], fdBRho, 1e-4); //theta final double tUp = baw.sCrit(s0, k, r, b, t + eps, sigma, isCall); final double tDown = baw.sCrit(s0, k, r, b, t - eps, sigma, isCall); final double fdTheta = (tUp - tDown) / 2 / eps; assertEquals("theta", sCAdj[3], fdTheta, 1e-6); //vega final double volUp = baw.sCrit(s0, k, r, b, t, sigma + eps, isCall); final double volDown = baw.sCrit(s0, k, r, b, t, sigma - eps, isCall); final double fdVega = (volUp - volDown) / 2 / eps; assertEquals("vega", sCAdj[4], fdVega, 1e-5); } } @Test public void adjointTest() { final BaroneAdesiWhaleyModel model = new BaroneAdesiWhaleyModel(); final double[] s0Set = new double[] {60, 90, 100, 110, 160 }; final double k = 100; final double r = 0.1; final double[] bSet = new double[] {-0.04, 0.0, 0.04, 0.11 }; final double sigma = 0.35; final double t = 0.5; final boolean[] tfSet = new boolean[] {true, false }; final double eps = 1e-5; for (final double s0 : s0Set) { for (final double b : bSet) { final double[] parms = new double[] {s0, k, r, b, t, sigma }; final int n = parms.length; for (final boolean isCall : tfSet) { final double call = model.price(s0, k, r, b, t, sigma, isCall); final double[] sense = model.getPriceAdjoint(s0, k, r, b, t, sigma, isCall); assertEquals("price " + s0 + " " + b + " " + isCall, call, sense[0], 1e-13); for (int i = 0; i < n; i++) { final double[] temp = Arrays.copyOf(parms, n); final double delta = (1 + Math.abs(parms[i])) * eps; temp[i] += delta; final double up = model.price(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], isCall); temp[i] -= 2 * delta; final double down = model.price(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], isCall); final double fd = (up - down) / 2 / delta; assertEquals(i + " " + k + " " + b + " " + isCall, fd, sense[i + 1], Math.abs(fd) * 1e-5); //System.out.println(i + " " + s0 + " " + b + " " + isCall + " " + fd + "\t" + sense[i + 1]); } } } } } /** * The Barone-Adesi Whaley model does not exactly obey the put-call transformation (Bjerksund and Stensland (1993)) */ @Test public void putCallTransformTest() { final double s0 = 110; final double t = 0.5; final double r = 0.1; final double sigma = 0.35; final double[] kSet = new double[] {60, 80, 100, 110, 120, 160 }; final double[] bSet = new double[] {-0.04, 0.0, 0.04, 0.11 }; final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); for (final double k : kSet) { for (final double b : bSet) { final double cprice = baw.price(s0, k, r, b, t, sigma, true); final double ccprice = baw.price(k, s0, r - b, -b, t, sigma, false); //price the call as a put assertEquals("strike " + k, cprice, ccprice, 2e-3 * cprice); } } } @Test public void impliedVolTest() { final double s0 = 110; final double t = 0.5; final double r = 0.1; final double sigma = 0.35; final double[] kSet = new double[] {60, 80, 100, 110, 120, 160 }; final double[] bSet = new double[] {-0.04, 0.0, 0.04, 0.11 }; final boolean[] tfSet = new boolean[] {true, false }; final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); for (final boolean isCall : tfSet) { for (final double k : kSet) { for (final double b : bSet) { final double p = baw.price(s0, k, r, b, t, sigma, isCall); final double vol = baw.impliedVolatility(p, s0, k, r, b, t, isCall); if (vol != 0.0) { assertEquals("k = " + k, sigma, vol, 1e-9); } } } } } @Test(enabled = false) public void impliedVol2Test() { final double s0 = 110; final double t = 0.5; final double r = 0.1; final double b = 0.07; final double sigma = 0.35; final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); final double fwd = s0 * Math.exp(b * t); final double df = Math.exp(-r * t); for (int j = 0; j < 2; j++) { final boolean isCall = j == 0; for (int i = 0; i < 50; i++) { final double k = 75 + 80 * i / 49.; final double fp = baw.price(s0, k, r, b, t, sigma, isCall) / df; final double biv = BlackFormulaRepository.impliedVolatility(fp, fwd, k, t, isCall); System.out.println(k + "\t" + biv); } System.out.println(); } } @Test(enabled = false) public void sCritTest() { final double s0 = 110; final double k = 110; ; final double r = 0.1; final double b = -0.04; final double sigma = 0.35; final boolean isCall = true; final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); for (int i = 0; i < 100; i++) { final double t = Math.exp(i / 15.0 - 6.0); final double sCrit = baw.sCrit(s0, k, r, b, t, sigma, isCall); System.out.println(t + "\t" + sCrit); } } @Test(enabled = false) public void priceSigmaTest() { final double s0 = 110; final double t = 0.5; final double r = 0.1; final double b = -0.04; final double k = 80; final BaroneAdesiWhaleyModel baw = new BaroneAdesiWhaleyModel(); for (int i = 0; i < 101; i++) { final double sigma = 0.05 + 0.7 * i / 100.; final double p = baw.price(s0, k, r, b, t, sigma, true); System.out.println(sigma + "\t" + p); } } }