EuropeanOptionMertonModelBoundary.java

package net.finmath.finitedifference.assetderivativevaluation.boundaries;

import net.finmath.finitedifference.assetderivativevaluation.models.FDMMertonModel;
import net.finmath.finitedifference.assetderivativevaluation.products.EuropeanOption;
import net.finmath.finitedifference.assetderivativevaluation.products.FiniteDifferenceEquityProduct;
import net.finmath.finitedifference.boundaries.BoundaryCondition;
import net.finmath.finitedifference.boundaries.StandardBoundaryCondition;
import net.finmath.modelling.products.CallOrPut;

/**
 * Boundary conditions for {@link EuropeanOption} under the
 * {@link FDMMertonModel}.
 *
 * <p>
 * The class follows the standard factory naming convention
 * {@code <ProductSimpleName><ModelCoreName>Boundary}, so that it can be
 * instantiated automatically by {@link FDBoundaryFactory}.
 * </p>
 *
 * <p>
 * Since the finite-difference Merton model is formulated in the stock variable,
 * the standard one-dimensional vanilla outer-domain asymptotics are used.
 * </p>
 *
 * @author Alessandro Gnoatto
 */
public class EuropeanOptionMertonModelBoundary implements FiniteDifferenceBoundary {

	/**
	 * The model.
	 */
	private final FDMMertonModel model;
	/**
	 * The epsilon.
	 */
	private static final double EPSILON = 1E-6;

	/**
	 * Creates the boundary condition associated with a given
	 * {@link FDMMertonModel}.
	 *
	 * @param model The Merton model used to determine risk-free and dividend
	 *              discount factors.
	 */
	public EuropeanOptionMertonModelBoundary(final FDMMertonModel model) {
		this.model = model;
	}

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

		final EuropeanOption option = (EuropeanOption) product;
		final CallOrPut sign = option.getCallOrPut();

		if (sign == CallOrPut.CALL) {
			return new BoundaryCondition[] {
					StandardBoundaryCondition.dirichlet(0.0)
			};
		}

		time = Math.max(time, EPSILON);

		final double discountFactorRiskFree = model.getRiskFreeCurve().getDiscountFactor(time);
		final double riskFreeRate = -Math.log(discountFactorRiskFree) / time;

		final double strike = option.getStrike();
		final double maturity = option.getMaturity();

		return new BoundaryCondition[] {
				StandardBoundaryCondition.dirichlet(
						strike * Math.exp(-riskFreeRate * (maturity - time))
						)
		};
	}

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

		final EuropeanOption option = (EuropeanOption) product;
		final CallOrPut sign = option.getCallOrPut();

		if (sign == CallOrPut.PUT) {
			return new BoundaryCondition[] {
					StandardBoundaryCondition.dirichlet(0.0)
			};
		}

		time = Math.max(time, EPSILON);

		final double discountFactorRiskFree = model.getRiskFreeCurve().getDiscountFactor(time);
		final double riskFreeRate = -Math.log(discountFactorRiskFree) / time;

		final double discountFactorDividend = model.getDividendYieldCurve().getDiscountFactor(time);
		final double dividendYieldRate = -Math.log(discountFactorDividend) / time;

		final double strike = option.getStrike();
		final double maturity = option.getMaturity();
		final double stock = stateVariables[0];

		final double dividendAdjustedStockPrice =
				stock * Math.exp(-dividendYieldRate * (maturity - time));

		return new BoundaryCondition[] {
				StandardBoundaryCondition.dirichlet(
						dividendAdjustedStockPrice
						- strike * Math.exp(-riskFreeRate * (maturity - time))
						)
		};
	}
}