FDMCevModel.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 Constant Elasticity of
 * Variance (CEV) model.
 *
 * <p>
 * Under the risk-neutral measure the CEV dynamics are typically written as
 * </p>
 * <p>
 * {@code dS_t = (r(t) - q(t)) S_t dt + sigma * S_t^beta dW_t}.
 * </p>
 *
 * <p>
 * In this class, {@link #getDrift(double, double...)} returns the
 * <em>absolute drift</em>,
 * and {@link #getFactorLoading(double, double...)} returns the <em>absolute
 * factor loading</em>.
 * Hence, for CEV we return
 * {@code sigma * S^(beta)}.
 * </p>
 *
 * @author Alessandro Gnoatto
 */
public class FDMCevModel implements FiniteDifferenceEquityModel {

	/**
	 * The initial value.
	 */
	private final double initialValue;
	/**
	 * The risk free curve.
	 */
	private final DiscountCurve riskFreeCurve;
	/**
	 * The dividend yield curve.
	 */
	private final DiscountCurve dividendYieldCurve;

	/**
	 * The sigma.
	 */
	private final double sigma;
	/**
	 * The beta.
	 */
	private final double beta;

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

	/**
	 * Minimum time used to avoid division by zero.
	 */
	private static final double MINIMUM_TIME = 1.0E-6;

	/**
	 * Creates a CEV model instance using explicit discount curves for {@code r}
	 * and {@code q}.
	 *
	 * @param initialValue Initial spot {@code S(0)}.
	 * @param riskFreeCurve Risk-free discount curve.
	 * @param dividendYieldCurve Dividend yield discount curve.
	 * @param sigma CEV volatility scale parameter {@code sigma}.
	 * @param beta CEV elasticity parameter {@code beta}.
	 * @param spaceTimeDiscretization The space-time discretization.
	 */
	public FDMCevModel(
			final double initialValue,
			final DiscountCurve riskFreeCurve,
			final DiscountCurve dividendYieldCurve,
			final double sigma,
			final double beta,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialValue = initialValue;
		this.riskFreeCurve = riskFreeCurve;
		this.dividendYieldCurve = dividendYieldCurve;
		this.sigma = sigma;
		this.beta = beta;
		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

	/**
	 * Creates a CEV model instance assuming zero dividend yield ({@code q =
	 * 0}),
	 * consistent with the constructor style of {@code FDMBlackScholesModel}.
	 *
	 * @param initialValue Initial spot {@code S(0)}.
	 * @param riskFreeCurve Risk-free discount curve.
	 * @param sigma CEV volatility scale parameter {@code sigma}.
	 * @param beta CEV elasticity parameter {@code beta}.
	 * @param spaceTimeDiscretization The space-time discretization.
	 */
	public FDMCevModel(
			final double initialValue,
			final DiscountCurve riskFreeCurve,
			final double sigma,
			final double beta,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialValue = initialValue;
		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.sigma = sigma;
		this.beta = beta;
		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

	/**
	 * Creates a CEV model instance using constant rates {@code r} and {@code
	 * q}.
	 *
	 * @param initialValue Initial spot {@code S(0)}.
	 * @param riskFreeRate Constant risk-free rate {@code r}.
	 * @param dividendYieldRate Constant dividend yield {@code q}.
	 * @param sigma CEV volatility scale parameter {@code sigma}.
	 * @param beta CEV elasticity parameter {@code beta}.
	 * @param spaceTimeDiscretization The space-time discretization.
	 */
	public FDMCevModel(
			final double initialValue,
			final double riskFreeRate,
			final double dividendYieldRate,
			final double sigma,
			final double beta,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialValue = initialValue;

		final double[] timesR = new double[] {0.0, 1.0 };
		final double[] zeroRatesR = new double[] {riskFreeRate, riskFreeRate };
		final InterpolationMethod interpolationMethodR = InterpolationMethod.LINEAR;
		final InterpolationEntity interpolationEntityR = InterpolationEntity.VALUE;
		final ExtrapolationMethod extrapolationMethodR = ExtrapolationMethod.CONSTANT;

		this.riskFreeCurve = DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
				"riskFreeCurve",
				null,
				timesR,
				zeroRatesR,
				interpolationMethodR,
				extrapolationMethodR,
				interpolationEntityR
				);

		final double[] timesQ = new double[] {0.0, 1.0 };
		final double[] zeroRatesQ = new double[] {dividendYieldRate, dividendYieldRate };
		final InterpolationMethod interpolationMethodQ = InterpolationMethod.LINEAR;
		final InterpolationEntity interpolationEntityQ = InterpolationEntity.VALUE;
		final ExtrapolationMethod extrapolationMethodQ = ExtrapolationMethod.CONSTANT;

		this.dividendYieldCurve = DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
				"dividendCurve",
				null,
				timesQ,
				zeroRatesQ,
				interpolationMethodQ,
				extrapolationMethodQ,
				interpolationEntityQ
				);

		this.sigma = sigma;
		this.beta = beta;
		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

	/**
	 * Creates a CEV model instance using constant {@code r} and assuming zero
	 * dividend yield ({@code q = 0}).
	 *
	 * @param initialValue Initial spot {@code S(0)}.
	 * @param riskFreeRate Constant risk-free rate {@code r}.
	 * @param sigma CEV volatility scale parameter {@code sigma}.
	 * @param beta CEV elasticity parameter {@code beta}.
	 * @param spaceTimeDiscretization The space-time discretization.
	 */
	public FDMCevModel(
			final double initialValue,
			final double riskFreeRate,
			final double sigma,
			final double beta,
			final SpaceTimeDiscretization spaceTimeDiscretization) {

		this.initialValue = initialValue;

		final double[] timesR = new double[] {0.0, 1.0 };
		final double[] zeroRatesR = new double[] {riskFreeRate, riskFreeRate };
		final InterpolationMethod interpolationMethodR = InterpolationMethod.LINEAR;
		final InterpolationEntity interpolationEntityR = InterpolationEntity.VALUE;
		final ExtrapolationMethod extrapolationMethodR = ExtrapolationMethod.CONSTANT;

		this.riskFreeCurve = DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
				"riskFreeCurve",
				null,
				timesR,
				zeroRatesR,
				interpolationMethodR,
				extrapolationMethodR,
				interpolationEntityR
				);

		final double[] timesQ = new double[] {0.0, 1.0 };
		final double[] zeroRatesQ = new double[] {0.0, 0.0 };
		final InterpolationMethod interpolationMethodQ = InterpolationMethod.LINEAR;
		final InterpolationEntity interpolationEntityQ = InterpolationEntity.VALUE;
		final ExtrapolationMethod extrapolationMethodQ = ExtrapolationMethod.CONSTANT;

		this.dividendYieldCurve = DiscountCurveInterpolation.createDiscountCurveFromAnnualizedZeroRates(
				"dividendCurve",
				null,
				timesQ,
				zeroRatesQ,
				interpolationMethodQ,
				extrapolationMethodQ,
				interpolationEntityQ
				);

		this.sigma = sigma;
		this.beta = beta;
		this.spaceTimeDiscretization = spaceTimeDiscretization;
	}

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

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

	/**
	 * Returns the initial spot {@code S(0)}.
	 *
	 * @return The initial spot.
	 */
	@Override
	public double[] getInitialValue() {
		return new double[] {initialValue};
	}

	/**
	 * Returns the CEV volatility scale parameter {@code sigma}.
	 *
	 * @return {@code sigma}.
	 */
	public double getSigma() {
		return sigma;
	}

	/**
	 * Returns the CEV elasticity parameter {@code beta}.
	 *
	 * @return {@code beta}.
	 */
	public double getBeta() {
		return beta;
	}

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

	@Override
	public double[] getDrift(final double time, final double... stateVariables) {
		final double effectiveTime = time == 0.0 ? MINIMUM_TIME : time;

		final double[] result = new double[1];

		final double rF = getRiskFreeCurve().getDiscountFactor(effectiveTime);
		final double riskFreeRate = -Math.log(rF) / effectiveTime;

		final double dY = getDividendYieldCurve().getDiscountFactor(effectiveTime);
		final double dividendYieldRate = -Math.log(dY) / effectiveTime;

		result[0] = (riskFreeRate - dividendYieldRate) * stateVariables[0];
		return result;
	}

	@Override
	public double[][] getFactorLoading(final double time, final double... stateVariables) {
		final double S = stateVariables.length > 0 ? stateVariables[0] : initialValue;

		final double loading;
		if (S <= 0.0) {
			loading = 0.0;
		} else {
			loading = sigma * Math.pow(S, beta);
		}

		return new double[][] {{loading } };
	}

	@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 FDMCevModel(
				initialValue,
				riskFreeCurve,
				dividendYieldCurve,
				sigma,
				beta,
				newSpaceTimeDiscretization
				);
	}
}