/**
* 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 org.apache.commons.lang.Validate;
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.ExtendedCoupledPDEDataBundle;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.volatility.local.LocalVolatilitySurfaceStrike;
import com.opengamma.analytics.math.function.Function;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.surface.FunctionalDoublesSurface;
/**
*
*/
@SuppressWarnings("deprecation")
public class LocalVolDensity {
/**
* Get the coefficients (a, b and c) for the PDE governing the evolution of the transition density for a single underlying (i.e. 1 spatial dimension). The PDE is of the form
* $frac{\partial V}{\partial t} + a(x,t)\frac{\partial^2V}{\partial x^2}+b(x,t)\frac{\partial V}{\partial x} +c(x,t)V=0$ where $V(x,t)$ is the density
* @param forward the forward curve
* @param localVol the local volatility surface (parameterised by strike)
* @return The coefficients a, b & c - which are all functions of time and asset value (space)
*/
public static ConvectionDiffusionPDE1DCoefficients getStandardCoefficients(final ForwardCurve forward, final LocalVolatilitySurfaceStrike localVol) {
final Function<Double, Double> a = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
final double s = ts[1];
final double sigma = localVol.getVolatility(t, s) * s;
return -0.5 * sigma * sigma;
}
};
final Function<Double, Double> b = new Function<Double, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
final double s = ts[1];
final double lvDiv = getLocalVolFirstDiv(localVol, t, s);
final double lv = localVol.getVolatility(t, s);
return s * (forward.getDrift(t) - 2 * lv * (s * lvDiv + lv));
}
};
final Function<Double, Double> c = new Function<Double, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
final double s = ts[1];
final double lv1Div = getLocalVolFirstDiv(localVol, t, s);
final double lv2Div = getLocalVolSecondDiv(localVol, t, s);
final double lv = localVol.getVolatility(t, s);
final double temp1 = (lv + s * lv1Div);
final double temp2 = lv * s * (s * lv2Div + 2 * lv1Div);
return forward.getDrift(t) - temp1 * temp1 - temp2;
}
};
// //using a log-normal distribution with a very small Standard deviation as a proxy for a Dirac delta
// final Function1D<Double, Double> initialCondition = new Function1D<Double, Double>() {
// private final double _volRootTOffset = 0.01;
//
// @Override
// public Double evaluate(final Double s) {
// if (s == 0) {
// return 0.0;
// }
// final double x = Math.log(s / forward.getSpot());
// final NormalDistribution dist = new NormalDistribution(0, _volRootTOffset);
// return dist.getPDF(x) / s;
// }
// };
return new ConvectionDiffusionPDE1DStandardCoefficients(FunctionalDoublesSurface.from(a), FunctionalDoublesSurface.from(b), FunctionalDoublesSurface.from(c));
}
/**
* Get the coefficients (a, b, c, $\alpha$ & $\beta$) for the PDE governing the evolution of the transition density for a single underlying (i.e. 1 spatial dimension).
* The PDE is of the form $frac{\partial V}{\partial t} + a(x,t)\frac{\alpha(x,t)\partial^2V}{\partial x^2}+b(x,t)\frac{\beta(x,t)\partial V}{\partial x} +c(x,t)V=0$ where $V(x,t)$ is the density
* @param forward the forward curve
* @param localVol the local volatility surface (parameterised by strike)
* @return The coefficients a, b, c, $\alpha$ & $\beta$ - which are all functions of time and asset value (space)
*/
public static ConvectionDiffusionPDE1DFullCoefficients getFullCoefficients(final ForwardCurve forward, final LocalVolatilitySurfaceStrike localVol) {
final Function<Double, Double> a = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
return -1.0;
}
};
final Function<Double, Double> alpha = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
final double s = ts[1];
final double temp = s * localVol.getVolatility(t, s);
return 0.5 * temp * temp;
}
};
final Function<Double, Double> b = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
final double s = ts[1];
return s * forward.getDrift(t);
}
};
final Function<Double, Double> beta = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
return 1.0;
}
};
final Function<Double, Double> c = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
return forward.getDrift(t);
}
};
return new ConvectionDiffusionPDE1DFullCoefficients(FunctionalDoublesSurface.from(a), FunctionalDoublesSurface.from(b), FunctionalDoublesSurface.from(c), FunctionalDoublesSurface.from(alpha),
FunctionalDoublesSurface.from(beta));
}
public static ExtendedCoupledPDEDataBundle getExtendedCoupledPDEDataBundle(final ForwardCurve forward, final LocalVolatilitySurfaceStrike localVol, final double lambda1, final double lambda2,
final double initialProb) {
final Function<Double, Double> a = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
return -1.0;
}
};
final Function<Double, Double> alpha = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
final double s = ts[1];
final double temp = s * localVol.getVolatility(t, s);
return 0.5 * temp * temp;
}
};
final Function<Double, Double> b = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
final double s = ts[1];
return s * forward.getDrift(t);
}
};
final Function<Double, Double> beta = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
return 1.0;
}
};
final Function<Double, Double> c = new Function<Double, Double>() {
@Override
public Double evaluate(final Double... ts) {
Validate.isTrue(ts.length == 2);
final double t = ts[0];
return forward.getDrift(t) + lambda1;
}
};
//using a log-normal distribution with a very small Standard deviation as a proxy for a Dirac delta
final Function1D<Double, Double> initialCondition = new Function1D<Double, Double>() {
private final double _volRootTOffset = 0.01;
@Override
public Double evaluate(final Double s) {
if (s == 0) {
return 0.0;
}
final double x = Math.log(s / forward.getSpot());
final NormalDistribution dist = new NormalDistribution(0, _volRootTOffset);
return initialProb * dist.getPDF(x) / s;
}
};
return new ExtendedCoupledPDEDataBundle(FunctionalDoublesSurface.from(a), FunctionalDoublesSurface.from(b), FunctionalDoublesSurface.from(c), FunctionalDoublesSurface.from(alpha),
FunctionalDoublesSurface.from(beta), lambda2, initialCondition);
}
//TODO handle with a central calculator
private static double getLocalVolFirstDiv(final LocalVolatilitySurfaceStrike localVol, final double t, final double s) {
final double eps = 1e-4;
final double up = localVol.getVolatility(t, s + eps);
final double down = localVol.getVolatility(t, s - eps);
return (up - down) / 2 / eps;
}
private static double getLocalVolSecondDiv(final LocalVolatilitySurfaceStrike localVol, final double t, final double s) {
final double eps = 1e-4;
final double up = localVol.getVolatility(t, s + eps);
final double mid = localVol.getVolatility(t, s);
final double down = localVol.getVolatility(t, s - eps);
return (up + down - 2 * mid) / eps / eps;
}
}