/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.finitedifference.applications;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
import com.opengamma.analytics.financial.model.finitedifference.BoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDE1DCoefficients;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDE1DFullCoefficients;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDE1DStandardCoefficients;
import com.opengamma.analytics.financial.model.finitedifference.DirichletBoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.ExponentialMeshing;
import com.opengamma.analytics.financial.model.finitedifference.HyperbolicMeshing;
import com.opengamma.analytics.financial.model.finitedifference.MeshingFunction;
import com.opengamma.analytics.financial.model.finitedifference.PDE1DDataBundle;
import com.opengamma.analytics.financial.model.finitedifference.PDEFullResults1D;
import com.opengamma.analytics.financial.model.finitedifference.PDEGrid1D;
import com.opengamma.analytics.financial.model.finitedifference.ThetaMethodFiniteDifference;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.volatility.local.DupireLocalVolatilityCalculator;
import com.opengamma.analytics.financial.model.volatility.local.LocalVolatilitySurfaceStrike;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRFormulaData;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRHaganVolatilityFunction;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceStrike;
import com.opengamma.analytics.math.curve.ConstantDoublesCurve;
import com.opengamma.analytics.math.function.Function;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.statistics.leastsquare.LeastSquareResultsWithTransform;
import com.opengamma.analytics.math.surface.FunctionalDoublesSurface;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
* Test.
*/
@Test(groups = TestGroup.UNIT)
public class TwoStateMarkovChainSABRFitterTest {
private static final PDE1DCoefficientsProvider PDE_PROVIDER = new PDE1DCoefficientsProvider();
private static final InitialConditionsProvider INITAL_CONDITION_PROVIDER = new InitialConditionsProvider();
private static final Function1D<Double, Double> ALPHA;
private static final double BETA = 0.5;
private static final double RHO = -0.0;
private static final Function1D<Double, Double> NU;
private static final double T = 5.0;
private static final double SPOT = 1.0;
private static final ForwardCurve FORWARD_CURVE;
//private static final YieldCurve YIELD_CURVE;
private static final double RATE = 0.0;
private static final Function<Double, Double> SABR_VOL_FUNCTION;
private static final List<double[]> EXPIRY_AND_STRIKES = new ArrayList<>();
private static final List<Pair<double[], Double>> SABR_VOLS;
static {
EXPIRY_AND_STRIKES.add(new double[] {1. / 12, 0.925 });
EXPIRY_AND_STRIKES.add(new double[] {1. / 12, 1.0 });
EXPIRY_AND_STRIKES.add(new double[] {1. / 12, 1.075 });
EXPIRY_AND_STRIKES.add(new double[] {0.5, 0.9 });
EXPIRY_AND_STRIKES.add(new double[] {0.5, 0.95 });
EXPIRY_AND_STRIKES.add(new double[] {0.5, 1.0 });
EXPIRY_AND_STRIKES.add(new double[] {0.5, 1.05 });
EXPIRY_AND_STRIKES.add(new double[] {0.5, 1.1 });
EXPIRY_AND_STRIKES.add(new double[] {1, 0.8 });
EXPIRY_AND_STRIKES.add(new double[] {1, 0.9 });
EXPIRY_AND_STRIKES.add(new double[] {1, 1.0 });
EXPIRY_AND_STRIKES.add(new double[] {1, 1.1 });
EXPIRY_AND_STRIKES.add(new double[] {2, 0.7 });
EXPIRY_AND_STRIKES.add(new double[] {2, 0.9 });
EXPIRY_AND_STRIKES.add(new double[] {2, 1.0 });
EXPIRY_AND_STRIKES.add(new double[] {2, 1.2 });
EXPIRY_AND_STRIKES.add(new double[] {2, 1.4 });
EXPIRY_AND_STRIKES.add(new double[] {3, 1.0 });
EXPIRY_AND_STRIKES.add(new double[] {4, 1.0 });
EXPIRY_AND_STRIKES.add(new double[] {5, 0.4 });
EXPIRY_AND_STRIKES.add(new double[] {5, 0.6 });
EXPIRY_AND_STRIKES.add(new double[] {5, 0.8 });
EXPIRY_AND_STRIKES.add(new double[] {5, 0.9 });
EXPIRY_AND_STRIKES.add(new double[] {5, 1.0 });
EXPIRY_AND_STRIKES.add(new double[] {5, 2.0 });
EXPIRY_AND_STRIKES.add(new double[] {5, 2.5 });
EXPIRY_AND_STRIKES.add(new double[] {5, 3.0 });
EXPIRY_AND_STRIKES.add(new double[] {5, 3.5 });
EXPIRY_AND_STRIKES.add(new double[] {5, 4.0 });
final Function1D<Double, Double> fwd = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double t) {
return SPOT * Math.exp(t * RATE);
}
};
FORWARD_CURVE = new ForwardCurve(fwd);
// YIELD_CURVE = new YieldCurve(ConstantDoublesCurve.from(RATE));
ALPHA = new Function1D<Double, Double>() {
double a = 0.0;
double b = 0.0;
double c = 0.0;
double d = 0.2;
@Override
public Double evaluate(final Double t) {
final double atmVol = (a + b * t) * Math.exp(-c * t) + d;
return atmVol * Math.pow(SPOT, 1 - BETA);
}
};
NU = new Function1D<Double, Double>() {
double a = 0.6;
double b = 0.0;
double c = 1.0;
double d = 0.3;
@Override
public Double evaluate(final Double t) {
return (a + b * t) * Math.exp(-c * t) + d;
}
};
SABR_VOL_FUNCTION = new Function<Double, Double>() {
SABRHaganVolatilityFunction hagan = new SABRHaganVolatilityFunction();
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double... tk) {
final double t = tk[0];
final double k = tk[1];
final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true);
final Function1D<SABRFormulaData, Double> func = hagan.getVolatilityFunction(option, FORWARD_CURVE.getForward(t));
final SABRFormulaData data = new SABRFormulaData(ALPHA.evaluate(t), BETA, RHO, NU.evaluate(t));
return func.evaluate(data);
}
};
SABR_VOLS = new ArrayList<>(EXPIRY_AND_STRIKES.size());
for (int i = 0; i < EXPIRY_AND_STRIKES.size(); i++) {
final double[] tk = EXPIRY_AND_STRIKES.get(i);
final Pair<double[], Double> pair = Pairs.of(tk, SABR_VOL_FUNCTION.evaluate(tk[0], tk[1]));
SABR_VOLS.add(pair);
}
}
@Test(enabled = false)
public void dumpSurfaceTest() {
PDEUtilityTools.printSurface("dumpSurfaceTest", FunctionalDoublesSurface.from(SABR_VOL_FUNCTION), 0, 5.0, SPOT / 4.0, 4.0 * SPOT);
}
@Test
(enabled = false)
public void test() {
final DoubleMatrix1D initialGuess = new DoubleMatrix1D(new double[] {0.2, 0.3, 0.2, 2.0, 0.95, 0.8 });
final TwoStateMarkovChainFitter fitter = new TwoStateMarkovChainFitter();
final LeastSquareResultsWithTransform res = fitter.fit(FORWARD_CURVE, SABR_VOLS, initialGuess);
System.out.println("chi^2:" + res.getChiSq() + "\n params: " + res.getModelParameters().toString());
}
//TODO this test does not work for the ConvectionDiffusionPDE1DStandardCoefficients case
@Test
(enabled = false)
public void fokkerPlankTest() {
final DupireLocalVolatilityCalculator cal = new DupireLocalVolatilityCalculator(1e-4);
final BlackVolatilitySurfaceStrike volSurface = new BlackVolatilitySurfaceStrike(FunctionalDoublesSurface.from(SABR_VOL_FUNCTION));
final LocalVolatilitySurfaceStrike localVol = cal.getLocalVolatility(volSurface, new ForwardCurve(SPOT, RATE));
// final ZZConvectionDiffusionPDEDataBundle db1 = LocalVolDensity.getConvectionDiffusionPDEDataBundle(FORWARD_CURVE, localVol);
// final ExtendedConvectionDiffusionPDEDataBundle db2 = LocalVolDensity.getExtendedConvectionDiffusionPDEDataBundle(FORWARD_CURVE, localVol);
final int tNodes = 50;
final int xNodes = 100;
final BoundaryCondition lower = new DirichletBoundaryCondition(0.0, 0.0);
final BoundaryCondition upper = new DirichletBoundaryCondition(0.0, 10 * FORWARD_CURVE.getForward(T));
final MeshingFunction timeMesh = new ExponentialMeshing(0, T, tNodes, 5.0);
final MeshingFunction spaceMesh = new HyperbolicMeshing(0.0, upper.getLevel(), SPOT, xNodes, 0.01);
final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh);
final ConvectionDiffusionPDE1DFullCoefficients pde1 = PDE_PROVIDER.getFokkerPlank(ConstantDoublesCurve.from(RATE), localVol);
final ConvectionDiffusionPDE1DStandardCoefficients pde2 = PDE_PROVIDER.getFokkerPlankInStandardCoefficients(ConstantDoublesCurve.from(RATE), localVol);
final Function1D<Double, Double> initalCondition = INITAL_CONDITION_PROVIDER.getLogNormalDensity(SPOT, 0.01, 0.2);
final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db1 = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde1, initalCondition, lower, upper, grid);
final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db2 = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde2, initalCondition, lower, upper, grid);
final ThetaMethodFiniteDifference thetaMethod = new ThetaMethodFiniteDifference(0.5, true);
final PDEFullResults1D res1 = (PDEFullResults1D) thetaMethod.solve(db1);
final PDEFullResults1D res2 = (PDEFullResults1D) thetaMethod.solve(db2);
PDEUtilityTools.printSurface("State 1 density", res1);
PDEUtilityTools.printSurface("State 2 density", res2);
}
//TODO need to have real tests here rather than print a lot of surfaces
@Test
(enabled = false)
public void localVolFitTest() {
final DoubleMatrix1D initialGuess = new DoubleMatrix1D(new double[] {0.2, 0.3, 0.2, 2.0, 0.95, 0.8 });
final TwoStateMarkovChainLocalVolFitter fitter = new TwoStateMarkovChainLocalVolFitter(true);
fitter.fit(FORWARD_CURVE, new BlackVolatilitySurfaceStrike(FunctionalDoublesSurface.from(SABR_VOL_FUNCTION)), SABR_VOLS, initialGuess);
// System.out.println("chi^2:" + res.getChiSq() + "\n params: " + res.getParameters().toString());
}
@Test(enabled = false)
public void localVolTest() {
final DupireLocalVolatilityCalculator cal = new DupireLocalVolatilityCalculator();
final BlackVolatilitySurfaceStrike volSurface = new BlackVolatilitySurfaceStrike(FunctionalDoublesSurface.from(SABR_VOL_FUNCTION));
final LocalVolatilitySurfaceStrike localVol = cal.getLocalVolatility(volSurface, new ForwardCurve(SPOT, RATE));
PDEUtilityTools.printSurface("localVolTest", localVol.getSurface(), 0, 5.0, SPOT / 4.0, SPOT * 4.0);
}
}