FDMHestonModel.java

package net.finmath.finitedifference.assetderivativevaluation.models;

import net.finmath.finitedifference.assetderivativevaluation.boundaries.FDBoundaryFactory;
import net.finmath.finitedifference.assetderivativevaluation.boundaries.FiniteDifferenceBoundary;
import net.finmath.finitedifference.assetderivativevaluation.products.FiniteDifferenceEquityProduct;
import net.finmath.finitedifference.boundaries.BoundaryCondition;
import net.finmath.finitedifference.grids.SpaceTimeDiscretization;
import net.finmath.marketdata.model.curves.CurveInterpolation.ExtrapolationMethod;
import net.finmath.marketdata.model.curves.CurveInterpolation.InterpolationEntity;
import net.finmath.marketdata.model.curves.CurveInterpolation.InterpolationMethod;
import net.finmath.marketdata.model.curves.DiscountCurve;
import net.finmath.marketdata.model.curves.DiscountCurveInterpolation;

/**
 * Finite difference model for option pricing under the Heston stochastic
 * volatility model.
 *
 * <p>
 * State variables are {@code (S, v)} where {@code S} is the spot and {@code v}
 * is the
 * instantaneous variance.
 * </p>
 *
 * <p>
 * The methods {@link #getDrift(double, double...)} and {@link
 * #getFactorLoading(double, double...)}
 * follow the conventions used in {@link FiniteDifferenceEquityModel}:
 * </p>
 * <ul>
 * <li>{@code getDrift} returns the vector of drifts for the state
 * variables.</li>
 * <li>{@code getFactorLoading} returns the matrix of factor loadings (here 2
 * factors), producing
 *       the correct covariance with correlation {@code rho}.</li>
 * </ul>
 *
 * <p>
 * Boundary conditions are provided through the {@link FiniteDifferenceBoundary}
 * interface methods,
 * via {@link FDBoundaryFactory}.
 * </p>
 *
 * @author Alessandro Gnoatto
 * @author Enrico De Vecchi
 */
public class FDMHestonModel implements FiniteDifferenceEquityModel {

	/**
	 * The initial spot.
	 */
	private final double initialSpot;
	/**
	 * The initial variance.
	 */
	private final double initialVariance;

	/**
	 * The risk free curve.
	 */
	private final DiscountCurve riskFreeCurve;
	/**
	 * The dividend yield curve.
	 */
	private final DiscountCurve dividendYieldCurve;

	// Heston parameters (risk-neutral)
	/**
	 * The kappa.
	 */
	private final double kappa;
	/**
	 * The theta v.
	 */
	private final double thetaV;
	/**
	 * The sigma.
	 */
	private final double sigma;    // vol-of-vol (often eta)
	/**
	 * The rho.
	 */
	private final double rho;

	/**
	 * The space time discretization.
	 */
	private final SpaceTimeDiscretization spaceTimeDiscretization;

	/**
	 * Constructs a Heston finite difference model for option pricing.
	 *
	 * @param initialSpot            Initial spot price.
	 * @param initialVariance        Initial variance.
	 * @param riskFreeCurve          Risk-free discount curve.
	 * @param dividendYieldCurve     Dividend yield discount curve.
	 * @param kappa                  Mean reversion speed of variance.
	 * @param thetaV                 Long-term mean of variance.
	 * @param sigma                  Vol-of-vol parameter.
	 * @param rho Correlation between spot and variance Brownian motions.
	 * @param spaceTimeDiscretization Grid object defining the spatial
	 *     discretization.
	 */
	public FDMHestonModel(
			final double initialSpot,
			final double initialVariance,
			final DiscountCurve riskFreeCurve,
			final DiscountCurve dividendYieldCurve,
			final double kappa,
			final double thetaV,
			final double sigma,
			final double rho,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialSpot = initialSpot;
		this.initialVariance = initialVariance;
		this.riskFreeCurve = riskFreeCurve;
		this.dividendYieldCurve = dividendYieldCurve;

		this.kappa = kappa;
		this.thetaV = thetaV;
		this.sigma = sigma;
		this.rho = rho;

		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

	/**
	 * Constructs a Heston finite difference model for option pricing without
	 * dividend yield curve
	 * (i.e. dividend yield is assumed to be zero).
	 *
	 * @param initialSpot            Initial spot price.
	 * @param initialVariance        Initial variance.
	 * @param riskFreeCurve          Risk-free discount curve.
	 * @param kappa                  Mean reversion speed of variance.
	 * @param thetaV                 Long-term mean of variance.
	 * @param sigma                  Vol-of-vol parameter.
	 * @param rho Correlation between spot and variance Brownian motions.
	 * @param spaceTimeDiscretization Grid object defining the spatial
	 *     discretization.
	 */
	public FDMHestonModel(
			final double initialSpot,
			final double initialVariance,
			final DiscountCurve riskFreeCurve,
			final double kappa,
			final double thetaV,
			final double sigma,
			final double rho,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialSpot = initialSpot;
		this.initialVariance = initialVariance;
		this.riskFreeCurve = riskFreeCurve;

		final double[] times = new double[] {0.0, 1.0};
		final double[] givenAnnualizedZeroRates = new double[] {0.0, 0.0};
		final InterpolationMethod interpolationMethod = InterpolationMethod.LINEAR;
		final InterpolationEntity interpolationEntity = InterpolationEntity.LOG_OF_VALUE_PER_TIME;
		final ExtrapolationMethod extrapolationMethod = ExtrapolationMethod.CONSTANT;

		this.dividendYieldCurve =
				DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
						"dividendCurve",
						null,
						times,
						givenAnnualizedZeroRates,
						interpolationMethod,
						extrapolationMethod,
						interpolationEntity);

		this.kappa = kappa;
		this.thetaV = thetaV;
		this.sigma = sigma;
		this.rho = rho;

		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

	/**
	 * Constructs a Heston finite difference model for option pricing from
	 * constant rates.
	 *
	 * @param initialSpot            Initial spot price.
	 * @param initialVariance        Initial variance.
	 * @param riskFreeRate           Constant risk-free rate.
	 * @param dividendYieldRate      Constant dividend yield rate.
	 * @param kappa                  Mean reversion speed of variance.
	 * @param thetaV                 Long-term mean of variance.
	 * @param sigma                  Vol-of-vol parameter.
	 * @param rho Correlation between spot and variance Brownian motions.
	 * @param spaceTimeDiscretization Grid object defining the spatial
	 *     discretization.
	 */
	public FDMHestonModel(
			final double initialSpot,
			final double initialVariance,
			final double riskFreeRate,
			final double dividendYieldRate,
			final double kappa,
			final double thetaV,
			final double sigma,
			final double rho,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialSpot = initialSpot;
		this.initialVariance = initialVariance;

		final double[] times = new double[] {0.0, 1.0};
		final InterpolationMethod interpolationMethod = InterpolationMethod.LINEAR;
		final InterpolationEntity interpolationEntity = InterpolationEntity.VALUE;
		final ExtrapolationMethod extrapolationMethod = ExtrapolationMethod.CONSTANT;

		final double[] givenAnnualizedZeroRates = new double[] {riskFreeRate, riskFreeRate};
		this.riskFreeCurve =
				DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
						"dividendCurve",
						null,
						times,
						givenAnnualizedZeroRates,
						interpolationMethod,
						extrapolationMethod,
						interpolationEntity);

		final double[] givenAnnualizedZeroRates1 = new double[] {dividendYieldRate, dividendYieldRate};
		this.dividendYieldCurve =
				DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
						"dividendCurve",
						null,
						times,
						givenAnnualizedZeroRates1,
						interpolationMethod,
						extrapolationMethod,
						interpolationEntity);

		this.kappa = kappa;
		this.thetaV = thetaV;
		this.sigma = sigma;
		this.rho = rho;

		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

	/**
	 * Constructs a Heston finite difference model for option pricing from a
	 * constant risk-free
	 * rate and zero dividend yield.
	 *
	 * @param initialSpot            Initial spot price.
	 * @param initialVariance        Initial variance.
	 * @param riskFreeRate           Constant risk-free rate.
	 * @param kappa                  Mean reversion speed of variance.
	 * @param thetaV                 Long-term mean of variance.
	 * @param sigma                  Vol-of-vol parameter.
	 * @param rho Correlation between spot and variance Brownian motions.
	 * @param spaceTimeDiscretization Grid object defining the spatial
	 *     discretization.
	 */
	public FDMHestonModel(
			final double initialSpot,
			final double initialVariance,
			final double riskFreeRate,
			final double kappa,
			final double thetaV,
			final double sigma,
			final double rho,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialSpot = initialSpot;
		this.initialVariance = initialVariance;

		final double[] times = new double[] {0.0, 1.0};
		final InterpolationMethod interpolationMethod = InterpolationMethod.LINEAR;
		final InterpolationEntity interpolationEntity = InterpolationEntity.VALUE;
		final ExtrapolationMethod extrapolationMethod = ExtrapolationMethod.CONSTANT;

		final double[] givenAnnualizedZeroRates = new double[] {riskFreeRate, riskFreeRate};
		this.riskFreeCurve =
				DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
						"dividendCurve",
						null,
						times,
						givenAnnualizedZeroRates,
						interpolationMethod,
						extrapolationMethod,
						interpolationEntity);

		final double[] givenAnnualizedZeroRates1 = new double[] {0.0, 0.0};
		this.dividendYieldCurve =
				DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
						"dividendCurve",
						null,
						times,
						givenAnnualizedZeroRates1,
						interpolationMethod,
						extrapolationMethod,
						interpolationEntity);

		this.kappa = kappa;
		this.thetaV = thetaV;
		this.sigma = sigma;
		this.rho = rho;

		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

	@Override
	public DiscountCurve getRiskFreeCurve() {
		return riskFreeCurve;
	}

	@Override
	public DiscountCurve getDividendYieldCurve() {
		return dividendYieldCurve;
	}

	/**
	 * Returns the initial value of the system of SDEs.
	 *
	 * @return The initial spot.
	 */
	@Override
	public double[] getInitialValue() {
		return new double[] {initialSpot, initialVariance};
	}

	/**
	 * Returns the initial variance value.
	 *
	 * @return The initial variance.
	 */
	/*public double getInitialVariance() {
		return initialVariance;
	}*/

	/**
	 * Returns the mean reversion speed of variance.
	 *
	 * @return The parameter {@code kappa}.
	 */
	public double getKappa() {
		return kappa;
	}

	/**
	 * Returns the long-term mean of variance.
	 *
	 * @return The parameter {@code thetaV}.
	 */
	public double getThetaV() {
		return thetaV;
	}

	/**
	 * Returns the vol-of-vol parameter.
	 *
	 * @return The parameter {@code sigma}.
	 */
	public double getSigma() {
		return sigma;
	}

	/**
	 * Returns the correlation between the Brownian motions.
	 *
	 * @return The parameter {@code rho}.
	 */
	public double getRho() {
		return rho;
	}

	@Override
	public SpaceTimeDiscretization getSpaceTimeDiscretization() {
		return this.spaceTimeDiscretization;
	}

	@Override
	public double[] getDrift(final double time, final double... stateVariables) {
		// stateVariables[0]=S, stateVariables[1]=v
		final double v = stateVariables.length > 1 ? stateVariables[1] : initialVariance;

		double t = time;
		if (t == 0) {
			t = 0.000001;
		}

		final double rF = getRiskFreeCurve().getDiscountFactor(t);
		final double r = -Math.log(rF) / t;

		final double dY = getDividendYieldCurve().getDiscountFactor(t);
		final double q = -Math.log(dY) / t;

		final double mu = (r - q) * stateVariables[0];		// drift for S
		final double muV = kappa * (thetaV - v);	// drift of the CIR

		return new double[] {mu, muV};
	}

	@Override
	public double[][] getFactorLoading(final double time, final double... stateVariables) {
		// Factors are independent. Correlation rho is embedded in the second
		// row.
		final double stock = stateVariables.length > 0 ? stateVariables[0] : initialSpot;
		final double variance = Math.max(0.0, stateVariables.length > 1 ? stateVariables[1] : initialVariance);
		final double sqrtV = Math.sqrt(variance);

		final double sqrtOneMinusRho2 = Math.sqrt(Math.max(0.0, 1.0 - rho * rho));

		// state dimension 2, factor dimension 2
		final double[][] b = new double[2][2];

		// dS = ... + S * sqrt(v) dW1
		b[0][0] = sqrtV * stock;	// percentage loading for S: will be multiplied by S in PDE operator
		b[0][1] = 0.0;

		// dv = ... + sigma * sqrt(v) ( rho dW1 + sqrt(1-rho^2) dW2 )
		b[1][0] = sigma * sqrtV * rho;
		b[1][1] = sigma * sqrtV * sqrtOneMinusRho2;

		return b;
	}

	/**
	 * Convenience method returning the covariance matrix {@code a = B B^T},
	 * derived from
	 * {@link #getFactorLoading(double, double...)}.
	 *
	 * @param time           Evaluation time.
	 * @param stateVariables State variables.
	 * @return The covariance matrix.
	 */
	public double[][] getCovariance(final double time, final double... stateVariables) {
		final double[][] factorLoading = getFactorLoading(time, stateVariables);

		final double a00 = factorLoading[0][0] * factorLoading[0][0] + factorLoading[0][1] * factorLoading[0][1];
		final double a01 = factorLoading[0][0] * factorLoading[1][0] + factorLoading[0][1] * factorLoading[1][1];
		final double a11 = factorLoading[1][0] * factorLoading[1][0] + factorLoading[1][1] * factorLoading[1][1];

		return new double[][] {
			{a00, a01},
			{a01, a11}
		};
	}

	@Override
	public BoundaryCondition[] getBoundaryConditionsAtLowerBoundary(
			final FiniteDifferenceEquityProduct product,
			final double time,
			final double... riskFactors) {

		final FiniteDifferenceBoundary boundary =
				FDBoundaryFactory.createBoundary(this, product);

		return boundary.getBoundaryConditionsAtLowerBoundary(product, time, riskFactors);
	}

	@Override
	public BoundaryCondition[] getBoundaryConditionsAtUpperBoundary(
			final FiniteDifferenceEquityProduct product,
			final double time,
			final double... riskFactors) {

		final FiniteDifferenceBoundary boundary =
				FDBoundaryFactory.createBoundary(this, product);

		return boundary.getBoundaryConditionsAtUpperBoundary(product, time, riskFactors);
	}

	@Override
	public FiniteDifferenceEquityModel getCloneWithModifiedSpaceTimeDiscretization(
			final SpaceTimeDiscretization newSpaceTimeDiscretization) {
		return new FDMHestonModel(
				initialSpot,
				initialVariance,
				riskFreeCurve,
				dividendYieldCurve,
				kappa,
				thetaV,
				sigma,
				rho,
				newSpaceTimeDiscretization
				);
	}
}