/**
* Copyright (C) 2009 - 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 static org.testng.AssertJUnit.assertTrue;
import java.util.Arrays;
import org.testng.annotations.Test;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.model.option.definition.AmericanVanillaOptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.StandardOptionDataBundle;
import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository;
import com.opengamma.analytics.financial.model.volatility.surface.VolatilitySurface;
import com.opengamma.analytics.math.curve.ConstantDoublesCurve;
import com.opengamma.analytics.math.differentiation.ScalarFieldFirstOrderDifferentiator;
import com.opengamma.analytics.math.differentiation.VectorFieldFirstOrderDifferentiator;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.math.statistics.distribution.BivariateNormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;
import com.opengamma.analytics.math.surface.ConstantDoublesSurface;
import com.opengamma.analytics.util.AssertMatrix;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Expiry;
/**
* Bjerksund and Stensland model test.
*/
@Test(groups = TestGroup.UNIT)
public class BjerksundStenslandModelTest {
private static final BjerksundStenslandModel model = new BjerksundStenslandModel();
private static final ScalarFieldFirstOrderDifferentiator SCALAR_FIELD_DIFF = new ScalarFieldFirstOrderDifferentiator();
private static final VectorFieldFirstOrderDifferentiator VEC_FIELD_DIFF = new VectorFieldFirstOrderDifferentiator();
private static final ProbabilityDistribution<double[]> BIVARIATE_NORMAL = new BivariateNormalDistribution();
private static final Function1D<DoubleMatrix1D, Double> CALL_PRICE_FUNC = new Function1D<DoubleMatrix1D, Double>() {
@Override
public Double evaluate(DoubleMatrix1D parms) {
double[] p = parms.getData();
return model.price(p[0], p[1], p[2], p[3], p[4], p[5], true);
}
};
private static final Function1D<DoubleMatrix1D, Double> PUT_PRICE_FUNC = new Function1D<DoubleMatrix1D, Double>() {
@Override
public Double evaluate(DoubleMatrix1D parms) {
double[] p = parms.getData();
return model.price(p[0], p[1], p[2], p[3], p[4], p[5], false);
}
};
private static final Function1D<DoubleMatrix1D,DoubleMatrix1D> CALL_SENSE_FUNC = new Function1D<DoubleMatrix1D, DoubleMatrix1D>() {
@Override
public DoubleMatrix1D evaluate(DoubleMatrix1D parms) {
double[] p = parms.getData();
double[] adj = model.getPriceAdjoint(p[0], p[1], p[2], p[3], p[4], p[5], true);
double[] temp = new double[6];
System.arraycopy(adj, 1, temp, 0, 6);
return new DoubleMatrix1D(temp);
}
};
private static final Function1D<DoubleMatrix1D,DoubleMatrix1D> PUT_SENSE_FUNC = new Function1D<DoubleMatrix1D, DoubleMatrix1D>() {
@Override
public DoubleMatrix1D evaluate(DoubleMatrix1D parms) {
double[] p = parms.getData();
double[] adj = model.getPriceAdjoint(p[0], p[1], p[2], p[3], p[4], p[5], false);
double[] temp = new double[6];
System.arraycopy(adj, 1, temp, 0, 6);
return new DoubleMatrix1D(temp);
}
};
private static final Function1D<DoubleMatrix1D, Boolean> DOMAIN = new Function1D<DoubleMatrix1D, Boolean>() {
@Override
public Boolean evaluate(DoubleMatrix1D parms) {
double[] x = parms.getData();
return x[0] >= 0.0 && x[1] >= 0.0 && x[4] >= 0.0 && x[5] >= 0.0;
}
};
private static final Function1D<DoubleMatrix1D, DoubleMatrix1D> CALL_ADJOINT_FD = SCALAR_FIELD_DIFF.differentiate(CALL_PRICE_FUNC, DOMAIN);
private static final Function1D<DoubleMatrix1D, DoubleMatrix1D> PUT_ADJOINT_FD = SCALAR_FIELD_DIFF.differentiate(PUT_PRICE_FUNC, DOMAIN);
@Test
public void priceTest() {
final double s0 = 120;
final double r = 0.08;
final double q = 0.12;
final double b = r - q;
final double k = 100.0;
final double t = 0.25;
final double sigma = 0.3;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double eurPrice = Math.exp(-r * t) * BlackFormulaRepository.price(s0 * Math.exp(b * t), k, t, sigma, true);
final double amPrice = bs.price(s0, k, r, b, t, sigma, true);
//System.out.println(eurPrice + "\t" + amPrice);
assertTrue(amPrice > eurPrice);
assertEquals(20.193913138412203, amPrice, 1e-15);
}
@Test
//(enabled = false)
public void priceAdjointBsRecapTest() {
final double s0 = 120;
final double r = -0.12;
final double[] costs = new double[] {-0.12, 0.12 };
final double k = 100.0;
final double t = 0.25;
final double[] sigmas = new double[] {0.0, 0.2, 0.5 };
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
for (final double sigma : sigmas) {
for (final double b : costs) {
final double resPrice = bs.price(s0, k, r, b, t, sigma, false);
final double[] resGamma = bs.getPriceDeltaGamma(s0, k, r, b, t, sigma, false);
assertEquals(b + "\t" + sigma, resPrice, resGamma[0], 1e-13);
}
}
}
/**
* Check the sensitivity calculated by getPriceAdjoint and getPriceDeltaGamma agree with the finite-difference
* calculation
*/
@Test
public void priceAdjointBsTest() {
final double s0 = 120;
final double r = -0.12;
final double[] costs = new double[] {-0.12, 0.12 };
final double k = 100.0;
final double t = 0.25;
final double[] sigmas = new double[] {0.0, 0.2, 0.25, 0.3 };
Function1D<DoubleMatrix1D, DoubleMatrix2D> fd2OrderFunc = VEC_FIELD_DIFF.differentiate(PUT_SENSE_FUNC, DOMAIN);
for (final double sigma : sigmas) {
for (final double b : costs) {
DoubleMatrix1D pVec = new DoubleMatrix1D(s0, k, r, b, t, sigma );
double price = PUT_PRICE_FUNC.evaluate(pVec);
double[] res = model.getPriceAdjoint(s0, k, r, b, t, sigma, false);
assertEquals(b + "\t" + sigma, price, res[0], Math.abs(price) * 1e-14);
double[] temp = new double[6];
System.arraycopy(res, 1, temp, 0, 6);
DoubleMatrix1D priceAdj = new DoubleMatrix1D(temp);
//compute adjoint by FD
DoubleMatrix1D fdPriceAdj = PUT_ADJOINT_FD.evaluate(pVec);
AssertMatrix.assertEqualsVectors(fdPriceAdj, priceAdj, 1e-8);
//check the this produces the correct price and delta, and check the gamma against fd (of the delta)
final double[] resGamma = model.getPriceDeltaGamma(s0, k, r, b, t, sigma, false);
assertEquals(b + "\t" + sigma, price, resGamma[0], Math.max(1.e-14, Math.abs(price) * 1e-14));
assertEquals(res[1], resGamma[1], Math.max(1.e-14, Math.abs(res[1]) * 1e-14));
double fd_gamma = fd2OrderFunc.evaluate(pVec).getEntry(0,0);
assertEquals(fd_gamma, resGamma[2], Math.abs(fd_gamma) * 1e-8);
}
}
}
/**
* Discontinuity of derivative value exists at b=r for call, where the switch from Bjerksund-Stensland model to Black model takes place.
* In our implementation we use the derivative values of the Black model at the transition point
*/
@Test
public void priceAdjointDiscontTest() {
final double s0 = 120;
final double r = -0.12;
final double[] costs = new double[] {-0.12 };
final double k = 100.0;
final double t = 0.25;
final double[] sigmas = new double[] { 0.3 };
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
for (final double sigma : sigmas) {
for (final double b : costs) {
final double[] params = new double[] {s0, k, r, b, t, sigma };
final int nParams = params.length;
final double[] res = bs.getPriceAdjoint(s0, k, r, b, t, sigma, true);
final double resPrice = bs.price(s0, k, r, b, t, sigma, true);
assertEquals(resPrice, res[0], Math.abs(resPrice) * 1e-14);
for (int i = 0; i < nParams; ++i) {
final double[] up = new double[nParams];
final double[] down = new double[nParams];
System.arraycopy(params, 0, up, 0, nParams);
System.arraycopy(params, 0, down, 0, nParams);
up[i] *= (1. + 1.e-4);
down[i] *= (1. - 1.e-4);
final double upRes = bs.price(up[0], up[1], up[2], up[3], up[4], up[5], true);
final double downRes = bs.price(down[0], down[1], down[2], down[3], down[4], down[5], true);
double fin = 0.;
if (i == 2) {
fin = (upRes - resPrice) / params[i] / 1.e-4;
} else {
if (i == 3) {
fin = (resPrice - downRes) / params[i] / 1.e-4;
} else {
fin = 0.5 * (upRes - downRes) / params[i] / 1.e-4;
}
}
assertEquals(i + "", fin, res[i + 1], Math.abs(params[i]) * 1e-3);
}
}
}
}
// [PLAT-2944]
@Test
public void earlyExciseTest() {
final double s0 = 10.0;
final double r = 0.0;
final double b = 0.05;
final double k = 13.0;
final double t = 0.25;
final double sigma = 0.3;
final boolean isCall = false;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double amPrice = bs.price(s0, k, r, b, t, sigma, isCall);
assertTrue(amPrice >= (k - s0));
}
@Test
public void adjointTest() {
final double[] s0Set = new double[] {60, 90, 100, 110, 160 };
final double k = 100;
final double[] rSet = new double[] {0.0, 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 BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double eps = 1e-5;
for (final double s0 : s0Set) {
for (final double r : rSet) {
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 price = bs.price(s0, k, r, b, t, sigma, isCall);
final double[] sense = bs.getPriceAdjoint(s0, k, r, b, t, sigma, isCall);
assertEquals("price " + s0 + " " + r + " " + b + " " + isCall, price, sense[0], 1e-13);
for (int i = 0; i < n; i++) {
final double delta = (1 + Math.abs(parms[i])) * eps;
final double[] temp = Arrays.copyOf(parms, n);
temp[i] += delta;
final double up = bs.price(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], isCall);
temp[i] -= 2 * delta;
final double down = bs.price(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], isCall);
// System.out.println("debug " + i + " " + s0 + " " + r + " " + b + " " + isCall + "\t" + up + "\t" + price + "\t" + down + "\t" + delta);
double fd;
if ((i == 3 && Math.abs(b) < delta) || (i == 2 && !isCall && r == 0.) /* || (i == 2 && Math.abs(r) < delta) */) {
// there is a discontinuity in the gradient at at b == 0 r != 0, and also for puts with r = 0 hence forward difference for the test
if (isCall) {
fd = (up - price) / delta;
} else {
fd = (price - down) / delta;
}
assertEquals(i + " " + s0 + " " + r + " " + b + " " + isCall, fd, sense[i + 1], Math.abs(fd) * 1e-4);
} else {
fd = (up - down) / 2 / delta;
if (!isCall && r == 0 && b > 0) {
assertEquals(i + " " + s0 + " " + r + " " + b + " " + isCall, fd, sense[i + 1], Math.abs(fd) * 1e-4);
} else {
assertEquals(i + " " + s0 + " " + r + " " + b + " " + isCall, fd, sense[i + 1], Math.abs(fd) * 1e-4);
}
}
}
}
}
}
}
}
@Test
// (enabled = false)
public void deltaGammaTest() {
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
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 };
for (final double s0 : s0Set) {
final double eps = 1e-5 * s0;
for (final double b : bSet) {
for (final boolean isCall : tfSet) {
final double price = bs.price(s0, k, r, b, t, sigma, isCall);
final double[] sense = bs.getPriceDeltaGamma(s0, k, r, b, t, sigma, isCall);
assertEquals("price " + s0 + " " + b, price, sense[0], 1e-13);
final double up = bs.price(s0 + eps, k, r, b, t, sigma, isCall);
final double down = bs.price(s0 - eps, k, r, b, t, sigma, isCall);
final double fd = (up - down) / 2 / eps;
final double fd2 = (up + down - 2 * price) / eps / eps;
assertEquals("delta " + s0 + " " + b, fd, sense[1], Math.abs(fd) * 1e-6);
assertEquals("gamma " + s0 + " " + b, fd2, sense[2], Math.abs(fd2) * 1e-4);
}
}
}
}
@Test
public void phiTest() {
final double s0 = 100;
final double[] x2Set = new double[] {130, 150, 170 };
final double r = 0.1;
final double[] bSet = new double[] {-0.04, 0.0, 0.04, r };
final double t1 = 0.5;
final double sigma = 0.35;
final double t = 2 * t1 / (Math.sqrt(5) - 1);
final double[] gammaSet = new double[] {0, 1, 0.67, 1.87 };
final double x1 = 133.0;
for (final double x2 : x2Set) {
for (final double b : bSet) {
for (final double gamma : gammaSet) {
final double[] parms = new double[] {s0, t, gamma, x1, x2, r, b, sigma };
final int n = parms.length;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double impA = bs.getPhi(s0, t1, gamma, x1, x2, r, b, sigma);
final double[] sense = bs.getPhiAdjoint(s0, t, gamma, x1, x2, r, b, sigma);
// System.out.println(impA + "\t" + sense[0]);
assertEquals(impA, sense[0], 1e-12);
final double eps = 1e-5;
for (int i = 0; i < 8; i++) {
final double[] temp = Arrays.copyOf(parms, n);
final double delta = (1 + Math.abs(parms[i])) * eps;
temp[i] += delta;
final double up = bs.getPhiAdjoint(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], temp[6], temp[7])[0];
temp[i] -= 2 * delta;
final double down = bs.getPhiAdjoint(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], temp[6], temp[7])[0];
final double fd = (up - down) / 2 / delta;
// System.out.println(fd + "\t" + sense[i + 1]);
assertEquals(i + " " + x2 + " " + b + " " + gamma, fd, sense[i + 1], Math.abs(fd) * 2e-8);
}
}
}
}
}
@Test
public void psiTest() {
final double s0 = 100;
final double[] kSet = new double[] {90, 100, 110 };
final double r = 0.1;
final double[] bSet = new double[] {-0.04, 0.04 };
final double t = 0.5;
final double sigma = 0.35;
final double r2 = 0.5 * (Math.sqrt(5) - 1);
final double t1 = r2 * t;
final double[] gammaSet = new double[] {0, 1, 0.67, 1.87 };
final double x1 = 133.0;
final double x2 = 140.2;
for (final double k : kSet) {
for (final double b : bSet) {
for (final double gamma : gammaSet) {
final double[] parms = new double[] {s0, t, gamma, k, x2, x1, r, b, sigma };
final int n = parms.length;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double impA = bs.getPsi(s0, t1, t, gamma, k, x2, x1, r, b, sigma);
final double[] sense = bs.getPsiAdjoint(s0, t, gamma, k, x2, x1, r, b, sigma);
assertEquals(impA, sense[0], 1e-12);
// System.out.println(impA + "\t" + sense[0]);
final double eps = 1e-5;
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 = bs.getPsiAdjoint(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], temp[6], temp[7], temp[8])[0];
temp[i] -= 2 * delta;
final double down = bs.getPsiAdjoint(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], temp[6], temp[7], temp[8])[0];
final double fd = (up - down) / 2 / delta;
// System.out.println(fd + "\t" + sense[i + 1]);
assertEquals(i + " " + k + " " + b + " " + gamma, fd, sense[i + 1], Math.abs(fd) * 1e-4); // TODO would expect better agreement than this
}
}
}
}
}
@Test
public void psiDeltaTest() {
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double s0 = 100;
final double[] kSet = new double[] {90, 100, 110 };
final double r = 0.1;
final double[] bSet = new double[] {-0.04, 0.04 };
final double t = 0.5;
final double sigma = 0.35;
final double r2 = 0.5 * (Math.sqrt(5) - 1);
final double t1 = r2 * t;
final double[] gammaSet = new double[] {0, 1, 0.67, 1.87 };
final double x1 = 133.0;
final double x2 = 140.2;
final double eps = 1e-5 * s0;
for (final double k : kSet) {
for (final double b : bSet) {
for (final double gamma : gammaSet) {
final double psi = bs.getPsi(s0, t1, t, gamma, k, x2, x1, r, b, sigma);
final double[] sense = bs.getPsiDelta(s0, t, gamma, k, x2, x1, r, b, sigma);
// double psi = sense[0];
assertEquals("psi", psi, sense[0], Math.abs(psi) * 1e-15);
final double up = bs.getPsi(s0 + eps, t1, t, gamma, k, x2, x1, r, b, sigma);
final double down = bs.getPsi(s0 - eps, t1, t, gamma, k, x2, x1, r, b, sigma);
final double fd = (up - down) / 2 / eps;
final double fd2 = (up + down - 2 * psi) / eps / eps;
assertEquals("psi delta", fd, sense[1], Math.abs(fd) * 1e-6);
assertEquals("psi gamma", fd2, sense[2], Math.abs(fd2) * 1e-4);
}
}
}
}
@Test
public void biVarNormTest() {
final double rho = Math.sqrt(0.5 * (Math.sqrt(5) - 1));
final double a = 1.2;
final double b = -0.6;
final double eps = 1e-5;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double cent = BIVARIATE_NORMAL.getCDF(new double[] {a, b, rho });
final double[] sense = bs.bivariateNormDiv(a, b, true);
final double aUp = BIVARIATE_NORMAL.getCDF(new double[] {a + eps, b, rho });
final double aDown = BIVARIATE_NORMAL.getCDF(new double[] {a - eps, b, rho });
final double bUp = BIVARIATE_NORMAL.getCDF(new double[] {a, b + eps, rho });
final double bDown = BIVARIATE_NORMAL.getCDF(new double[] {a, b - eps, rho });
final double aUpbUp = BIVARIATE_NORMAL.getCDF(new double[] {a + eps, b + eps, rho });
final double aDownbDown = BIVARIATE_NORMAL.getCDF(new double[] {a - eps, b - eps, rho });
final double aUpbDown = BIVARIATE_NORMAL.getCDF(new double[] {a + eps, b - eps, rho });
final double aDownbUp = BIVARIATE_NORMAL.getCDF(new double[] {a - eps, b + eps, rho });
// 1st
double fd = (aUp - aDown) / 2 / eps;
assertEquals("dB/da", fd, sense[0], Math.abs(fd) * 1e-5);
fd = (bUp - bDown) / 2 / eps;
assertEquals("dB/db", fd, sense[1], Math.abs(fd) * 1e-5);
// 2nd
fd = (aUp + aDown - 2 * cent) / eps / eps;
assertEquals("d^2B/da^2", fd, sense[2], Math.abs(fd) * 1e-4);
fd = (bUp + bDown - 2 * cent) / eps / eps;
assertEquals("d^2B/db^2", fd, sense[3], Math.abs(fd) * 1e-5);
fd = (aUpbUp + aDownbDown - aUpbDown - aDownbUp) / 4 / eps / eps;
assertEquals("d^2B/dadb", fd, sense[4], Math.abs(fd) * 2e-4);
}
@Test
public void alphaTest() {
final double k = 123;
final double x = 204;
final double beta = 2.3;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double[] sense = bs.getAlphaAdjoint(k, x, beta);
final double[] parms = new double[] {k, x, beta };
final int n = parms.length;
final double eps = 1e-5;
for (int i = 0; i < n; i++) {
final double[] temp = Arrays.copyOf(parms, n);
temp[i] += eps;
final double up = bs.getAlphaAdjoint(temp[0], temp[1], temp[2])[0];
temp[i] -= 2 * eps;
final double down = bs.getAlphaAdjoint(temp[0], temp[1], temp[2])[0];
final double fd = (up - down) / 2 / eps;
assertEquals(fd, sense[i + 1], Math.abs(fd) * 1e-8);
}
}
@Test
public void betaTest() {
final double r = 0.1;
final double b = 0.04;
final double sigma = 0.3;
final double sigmaSq = sigma * sigma;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double[] sense = bs.getBetaAdjoint(r, b, sigmaSq);
final double[] parms = new double[] {r, b, sigmaSq };
final int n = parms.length;
final double eps = 1e-5;
for (int i = 0; i < n; i++) {
final double[] temp = Arrays.copyOf(parms, n);
temp[i] += eps;
final double up = bs.getBetaAdjoint(temp[0], temp[1], temp[2])[0];
temp[i] -= 2 * eps;
final double down = bs.getBetaAdjoint(temp[0], temp[1], temp[2])[0];
final double fd = (up - down) / 2 / eps;
assertEquals(fd, sense[i + 1], Math.abs(fd) * 1e-8);
}
}
@Test
public void lambdaTest() {
final double[] gammaSet = new double[] {0, 1, 0.9, 2.3 };
final double r = 0.1;
final double[] bSet = {-0.03, 0, 0.04, r };
final double sigma = 0.3;
final double sigmaSq = sigma * sigma;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
for (final double gamma : gammaSet) {
for (final double b : bSet) {
final double[] sense = bs.getLambdaAdjoint(gamma, r, b, sigmaSq);
final double[] parms = new double[] {gamma, r, b, sigmaSq };
final int n = parms.length;
final double eps = 1e-5;
for (int i = 0; i < n; i++) {
final double[] temp = Arrays.copyOf(parms, n);
temp[i] += eps;
final double up = bs.getLambdaAdjoint(temp[0], temp[1], temp[2], temp[3])[0];
temp[i] -= 2 * eps;
final double down = bs.getLambdaAdjoint(temp[0], temp[1], temp[2], temp[3])[0];
final double fd = (up - down) / 2 / eps;
assertEquals(fd, sense[i + 1], Math.abs(fd) * 1e-8);
}
}
}
}
@Test
public void kappaTest() {
final double[] gammaSet = new double[] {0, 1, 0.9, 2.3 };
final double[] bSet = {-0.03, 0, 0.04 };
final double sigma = 0.3;
final double sigmaSq = sigma * sigma;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
for (final double gamma : gammaSet) {
for (final double b : bSet) {
final double[] sense = bs.getKappaAdjoint(gamma, b, sigmaSq);
final double[] parms = new double[] {gamma, b, sigmaSq };
final int n = parms.length;
final double eps = 1e-5;
for (int i = 0; i < n; i++) {
final double[] temp = Arrays.copyOf(parms, n);
temp[i] += eps;
final double up = bs.getKappaAdjoint(temp[0], temp[1], temp[2])[0];
temp[i] -= 2 * eps;
final double down = bs.getKappaAdjoint(temp[0], temp[1], temp[2])[0];
final double fd = (up - down) / 2 / eps;
assertEquals(fd, sense[i + 1], Math.abs(fd) * 2e-8);
}
}
}
}
@Test
public void i1Test() {
final double k = 100.04;
final double[] rSet = {-0.03, 0.1 };
final double[] bb = new double[] {-0.03, 0.04 };
final double sigma = 0.3;
final double t = 0.3;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
for (final double r : rSet) {
for (final double b : bb) {
if (r >= b) {
final double[] sense = bs.getI1Adjoint(k, r, b, sigma, t);
// System.out.println(sense[0]);
final double[] parms = new double[] {k, r, b, sigma, t };
final int n = parms.length;
final double eps = 1e-5;
for (int i = 0; i < n; i++) {
final double[] temp = Arrays.copyOf(parms, n);
temp[i] += eps;
final double up = bs.getI1Adjoint(temp[0], temp[1], temp[2], temp[3], temp[4])[0];
temp[i] -= 2 * eps;
final double down = bs.getI1Adjoint(temp[0], temp[1], temp[2], temp[3], temp[4])[0];
final double fd = (up - down) / 2 / eps;
// System.out.println(up + "\t" + down);
assertEquals(i + "\t" + r + "\t" + b, fd, sense[i + 1], Math.abs(fd) * 1e-5);
}
}
}
}
}
@Test
public void i2Test() {
final double k = 100.04;
final double r = 0.1;
final double[] bb = new double[] {-0.03, 0.04 };
final double sigma = 0.3;
final double t = 1.3;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
for (final double b : bb) {
final double[] sense = bs.getI2Adjoint(k, r, b, sigma, t);
final double[] parms = new double[] {k, r, b, sigma, t };
final int n = parms.length;
final double eps = 1e-5;
for (int i = 0; i < n; i++) {
final double[] temp = Arrays.copyOf(parms, n);
temp[i] += eps;
final double up = bs.getI2Adjoint(temp[0], temp[1], temp[2], temp[3], temp[4])[0];
temp[i] -= 2 * eps;
final double down = bs.getI2Adjoint(temp[0], temp[1], temp[2], temp[3], temp[4])[0];
final double fd = (up - down) / 2 / eps;
assertEquals(fd, sense[i + 1], Math.abs(fd) * 1e-7);
}
}
}
@Test
public void phiDeltaTest() {
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double s0 = 100;
final double[] x2Set = new double[] {130, 150, 170 };
final double r = 0.1;
final double[] bSet = new double[] {-0.04, 0.0, 0.04, r };
final double t1 = 0.5;
final double sigma = 0.35;
final double t = 2 * t1 / (Math.sqrt(5) - 1);
final double[] gammaSet = new double[] {0, 1, 0.67, 1.87 };
final double x1 = 133.0;
final double eps = s0 * 1e-5;
for (final double x2 : x2Set) {
for (final double b : bSet) {
for (final double gamma : gammaSet) {
final double phi = bs.getPhi(s0, t1, gamma, x1, x2, r, b, sigma);
final double[] sense = bs.getPhiDelta(s0, t, gamma, x1, x2, r, b, sigma);
assertEquals(phi, sense[0], Math.abs(phi) * 1e-15);
final double up = bs.getPhi(s0 + eps, t1, gamma, x1, x2, r, b, sigma);
final double down = bs.getPhi(s0 - eps, t1, gamma, x1, x2, r, b, sigma);
final double fd = (up - down) / 2 / eps;
final double fd2 = (up + down - 2 * phi) / eps / eps;
assertEquals(fd, sense[1], Math.abs(fd) * 2e-8);
assertEquals(fd2, sense[2], Math.abs(fd2) * 1e-5);
}
}
}
}
@Test
public void function1DTest() {
final double[] s0Set = new double[] {60, 90, 100, 110, 160 };
final double k = 100;
final double[] rSet = new double[] {0.0, 0.1 };
final double[] bSet = new double[] {-0.04, 0.0, 0.04, 0.11 };
final double sigma = 0.35;
final Expiry expiry = new Expiry(DateUtils.getDateOffsetWithYearFraction(DateUtils.getUTCDate(2011, 7, 1), 0.5));
final double t = 0.5;
final boolean[] tfSet = new boolean[] {true, false };
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
for (final double s0 : s0Set) {
for (final double r : rSet) {
for (final double b : bSet) {
for (final boolean isCall : tfSet) {
final double price = bs.price(s0, k, r, b, t, sigma, isCall);
final AmericanVanillaOptionDefinition option = new AmericanVanillaOptionDefinition(k, expiry, isCall);
final Function1D<StandardOptionDataBundle, Double> pFunc = bs.getPricingFunction(option);
final StandardOptionDataBundle dataBundle = new StandardOptionDataBundle(YieldCurve.from(ConstantDoublesCurve.from(r)), b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), s0,
DateUtils.getUTCDate(2011, 7, 1));
final double priceFunc = pFunc.evaluate(dataBundle);
assertEquals(price, priceFunc, 1e-16);
}
}
}
}
}
@Test
public void impliedVolSimpletest() {
final double modSpot = 30.405;
final double strike = 30.0;
final double discountRate = 0.0023576132185433372;
final double costOfCarry = 0.0023576132185433372;
final double timeToExpiry = 0.010958904109589041;
final double optionPrice = 0.275;
final boolean isCall = false;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
final double vol = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall);
final double vol1 = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall, 0.);
final double vol2 = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall, 0.0001);
final double vol3 = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall, 0.001);
final double vol4 = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall, 0.01);
final double vol5 = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall, 0.15);
final double vol6 = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall, 0.5);
final double vol7 = bs.impliedVolatility(optionPrice, modSpot, strike, discountRate, costOfCarry, timeToExpiry, isCall, 1.);
assertEquals(vol, vol1, 1e-9);
assertEquals(vol, vol2, 1e-9);
assertEquals(vol, vol3, 1e-9);
assertEquals(vol, vol4, 1e-9);
assertEquals(vol, vol5, 1e-9);
assertEquals(vol, vol6, 1e-9);
assertEquals(vol, vol7, 1e-9);
assertEquals(optionPrice, bs.price(modSpot, strike, discountRate, costOfCarry, timeToExpiry, vol, isCall), 1.e-5);
}
@Test
void zeroVolTest() {
double spot = 100;
double strike = 96;
double r = 0.01;
double q = 0.07;
double b = r - q;
double t = 2.0;
boolean isCall = false;
final BjerksundStenslandModel bs = new BjerksundStenslandModel();
double price = bs.price(spot, strike, r, b, t, 0.0, isCall);
double expected = Math.exp(-r * t) * Math.max(strike - spot * Math.exp(b * t), 0.0);
assertEquals(expected, price, 1e-14);
}
}