/**
* 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.fourier;
import static com.opengamma.analytics.math.ComplexMathUtils.exp;
import static org.testng.AssertJUnit.assertEquals;
import java.util.ArrayList;
import java.util.List;
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.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.volatility.BlackImpliedVolatilityFormula;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.number.ComplexNumber;
import com.opengamma.util.test.TestGroup;
/**
* Test.
*/
@Test(groups = TestGroup.UNIT)
public class HestonFourierPricerTest {
private static final double FORWARD = 0.04;
private static final double T = 2.0;
private static final double DF = 0.93;
private static final BlackImpliedVolatilityFormula BLACK_IMPLIED_VOL = new BlackImpliedVolatilityFormula();
@Test
public void testLowVolOfVol() {
final double sigma = 0.36;
final double kappa = 1.0; // mean reversion speed
final double theta = sigma * sigma; // reversion level
final double vol0 = theta; // start level
final double omega = 0.001; // vol-of-vol
final double rho = -0.3; // correlation
final MartingaleCharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
final FourierPricer pricer = new FourierPricer();
for (int i = 0; i < 21; i++) {
final double k = 0.2 + 3.0 * i / 20.0;
final EuropeanVanillaOption option = new EuropeanVanillaOption(k * FORWARD, T, true);
final BlackFunctionData data = new BlackFunctionData(FORWARD, DF, 0);
final double price = pricer.price(data, option, heston, -0.5, 1e-6);
final double impVol = BLACK_IMPLIED_VOL.getImpliedVolatility(data, option, price);
assertEquals(sigma, impVol, 1e-3);
}
}
@Test(enabled = false)
public void testHestonCE() {
final double kappa = 1.0; // mean reversion speed
final double theta = 0.16; // reversion level
final double vol0 = theta; // start level
final double omega = 2; // vol-of-vol
final double rho = -0.8; // correlation
final CharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
for (int j = 0; j < 101; j++) {
final double y = -3. + j * 6. / 100.0;
System.out.print("\t" + y);
}
System.out.print("\n");
for (int i = 0; i < 101; i++) {
final double x = -3. + i * 6. / 100.0;
System.out.print(x + "\t");
for (int j = 0; j < 101; j++) {
final double y = -3. + j * 6. / 100.0;
final ComplexNumber res = heston.getValue(new ComplexNumber(x, y), 0.25);
System.out.print(res.getReal() + "\t");
}
System.out.print("\n");
}
}
@Test(enabled = false)
public void testIntegrandHeston() {
final double alpha = 0.75;
final double kappa = 1.0; // mean reversion speed
final double theta = 0.16; // reversion level
final double vol0 = theta; // start level
final double omega = 2; // vol-of-vol
final double rho = -0.8; // correlation
final double t = 1.0;// / 52.0;
final MartingaleCharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
final EuropeanPriceIntegrand integrand = new EuropeanPriceIntegrand(heston, alpha, true);
final BlackFunctionData data = new BlackFunctionData(1, 1, 0.5);
for (int i = 0; i < 201; i++) {
final double x = -0. + i * 80. / 200.0;
final EuropeanVanillaOption option = new EuropeanVanillaOption(2, t, true);
final Double res = integrand.getFunction(data, option).evaluate(x);
System.out.println(x + "\t" + res);
}
}
@Test(enabled = false)
public void testEuropeanCallFTHeston() {
final double alpha = -0.4;
final double kappa = 1.0; // mean reversion speed
final double theta = 0.16; // reversion level
final double vol0 = theta; // start level
final double omega = 2; // vol-of-vol
final double rho = -0.8; // correlation
final double t = 1 / 52.0;
final MartingaleCharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
final EuropeanCallFourierTransform integrand = new EuropeanCallFourierTransform(heston);
final Function1D<ComplexNumber, ComplexNumber> func = integrand.getFunction(t);
for (int i = 0; i < 201; i++) {
final double x = -0. + i * 20. / 200.0;
final ComplexNumber res = func.evaluate((new ComplexNumber(x, alpha)));
System.out.println(x + "\t" + res.getReal() + "\t" + res.getImaginary());
}
}
@Test
public void testHeston() {
final double alpha = 0.75;
// parameters from the paper Not-so-complex logarithms in the Heston model
final double kappa = 1.0; // mean reversion speed
final double theta = 0.16; // reversion level
final double vol0 = theta; // start level
final double omega = 2; // vol-of-vol
final double rho = -0.8; // correlation
final double t = 1 / 12.0;
final MartingaleCharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
final FourierPricer pricer = new FourierPricer();
final BlackFunctionData data = new BlackFunctionData(1, 1, 0);
for (int i = 0; i < 11; i++) {
final double k = 0.5 + 1.0 * i / 10.0;
final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true);
final double price = pricer.price(data, option, heston, alpha, 1e-8);
BLACK_IMPLIED_VOL.getImpliedVolatility(data, option, price);
// System.out.println(k + "\t" + impVol);
}
}
@Test
public void testHestonModelGreeks() {
final FourierModelGreeks modelGreek = new FourierModelGreeks();
final double alpha = -0.5;
final double kappa = 1.0; // mean reversion speed
final double theta = 0.16; // reversion level
final double vol0 = theta; // start level
final double omega = 2; // vol-of-vol
final double rho = -0.8; // correlation
final double forward = 1.0;
final double t = 1 / 12.0;
final MartingaleCharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
final FourierPricer pricer = new FourierPricer();
final BlackFunctionData data = new BlackFunctionData(forward, 1, 0);
boolean isCall;
for (int i = 0; i < 11; i++) {
final double k = 0.7 + 0.6 * i / 10.0;
isCall = k >= forward;
final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, isCall);
final double[] sense = modelGreek.getGreeks(data, option, heston, alpha, 1e-12);
final double price = pricer.price(data, option, heston, alpha, 1e-8);
final double[] fdSense = finiteDifferenceModelGreeks((HestonCharacteristicExponent) heston, pricer, data, option);
// System.out.println(k + "\t" + price + "\t" + fdSense[0] / price + "\t" + fdSense[1] / price + "\t" + fdSense[2] / price + "\t" + fdSense[3] / price + "\t" + fdSense[4] / price
// + "\t" + sense[0] / price + "\t" + sense[1] / price + "\t" + sense[2] / price + "\t" + sense[3] / price + "\t" + sense[4] / price);
for(int index =0;index<5;index++) {
assertEquals( fdSense[index],sense[index],1e-3*price);
}
}
}
@Test(enabled=false)
public void testHestonModelGreeksIntegrand() {
final FourierModelGreeks modelGreek = new FourierModelGreeks();
final double alpha = -0.75;
final double kappa = 1.0; // mean reversion speed
final double theta = 0.16; // reversion level
final double vol0 = theta; // start level
final double omega = 2; // vol-of-vol
final double rho = -0.8; // correlation
final double forward = 1.0;
final double strike = 1.4;
final double t = 1 / 12.0;
final MartingaleCharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
final double k = Math.log(strike / forward);
final List<Function1D<Double, Double>> funcs = new ArrayList<>(5);
for (int index = 0; index < 5; index++) {
funcs.add(modelGreek.getIntegrandFunction(heston.getAdjointFunction(t), alpha, k, index));
}
for (int i = 0; i < 201; i++) {
final double x = 0.0 + 25.0 * i / 200;
System.out.print(x);
for (int index = 0; index < 5; index++) {
final double value = funcs.get(index).evaluate(x);
System.out.print("\t" + value);
}
System.out.print("\n");
}
}
@Test(enabled=false)
public void testHestonModelGreeksCE() {
final double alpha = -0.75;
final double kappa = 1.0; // mean reversion speed
final double theta = 0.16; // reversion level
final double vol0 = theta; // start level
final double omega = 2; // vol-of-vol
final double rho = -0.8; // correlation
final double t = 1 / 12.0;
final CharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho);
for (int i = 0; i < 201; i++) {
final double x = 0.0 + 250.0 * i / 200;
final ComplexNumber z = new ComplexNumber(x, -(1 + alpha));
final ComplexNumber[] res = heston.getCharacteristicExponentAdjoint(z, t);
System.out.print(x);
for (final ComplexNumber re : res) {
final double value = exp(re).getReal();
System.out.print("\t" + value);
}
System.out.print("\n");
}
}
public static double[] finiteDifferenceModelGreeks(final HestonCharacteristicExponent ce, final FourierPricer pricer, final BlackFunctionData data, final EuropeanVanillaOption option) {
final double eps = 1e-5;
final double tol = 1e-13;
final double alpha = -0.5;
final double[] res = new double[5];
//kappa
HestonCharacteristicExponent ceTemp = new HestonCharacteristicExponent(ce.getKappa() + eps, ce.getTheta(), ce.getVol0(), ce.getOmega(), ce.getRho());
double up = pricer.price(data, option, ceTemp, alpha,tol);
ceTemp = new HestonCharacteristicExponent(ce.getKappa() - eps, ce.getTheta(), ce.getVol0(), ce.getOmega(), ce.getRho());
double down = pricer.price(data, option, ceTemp, alpha, tol);
res[0] = (up - down) / 2 / eps;
//theta
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta() + eps, ce.getVol0(), ce.getOmega(), ce.getRho());
up = pricer.price(data, option, ceTemp, alpha, tol);
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta() - eps, ce.getVol0(), ce.getOmega(), ce.getRho());
down = pricer.price(data, option, ceTemp, alpha, tol);
res[1] = (up - down) / 2 / eps;
//vol0
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta(), ce.getVol0() + eps, ce.getOmega(), ce.getRho());
up = pricer.price(data, option, ceTemp, alpha, tol);
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta(), ce.getVol0() - eps, ce.getOmega(), ce.getRho());
down = pricer.price(data, option, ceTemp, alpha, tol);
res[2] = (up - down) / 2 / eps;
//omega
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta(), ce.getVol0(), ce.getOmega() + eps, ce.getRho());
up = pricer.price(data, option, ceTemp, alpha, tol);
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta(), ce.getVol0(), ce.getOmega() - eps, ce.getRho());
down = pricer.price(data, option, ceTemp, alpha, tol);
res[3] = (up - down) / 2 / eps;
//rho
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta(), ce.getVol0(), ce.getOmega(), ce.getRho() + eps);
up = pricer.price(data, option, ceTemp, alpha, tol);
ceTemp = new HestonCharacteristicExponent(ce.getKappa(), ce.getTheta(), ce.getVol0(), ce.getOmega(), ce.getRho() - eps);
down = pricer.price(data, option, ceTemp, alpha, tol);
res[4] = (up - down) / 2 / eps;
return res;
}
}