FiniteDifferenceExerciseUtil.java

package net.finmath.finitedifference;

import java.util.Set;
import java.util.TreeSet;

import net.finmath.modelling.AmericanExercise;
import net.finmath.modelling.Exercise;
import net.finmath.time.TimeDiscretization;
import net.finmath.time.TimeDiscretizationFromArray;

/**
 * Utility methods adapting a general {@link Exercise} specification to
 * finite-difference solvers working in time-to-maturity coordinates.
 *
 * <p>
 * Finite-difference solvers in this project use time-to-maturity
 * {@code tau = maturity - t}. Exercise specifications are given in running time
 * {@code t}. This class performs the required conversion and can refine the
 * time discretization so that all Bermudan exercise dates are represented
 * exactly.
 * </p>
 *
 * @author Alessandro Gnoatto
 */
public final class FiniteDifferenceExerciseUtil {

	/**
	 * The time tolerance.
	 */
	private static final double TIME_TOLERANCE = 1E-12;

	private FiniteDifferenceExerciseUtil() {
	}

	/**
	 * Returns the exercise times in time-to-maturity coordinates.
	 *
	 * @param exercise The exercise specification.
	 * @return The exercise times in tau = maturity - t.
	 */
	public static double[] getExerciseTimesToMaturity(final Exercise exercise) {
		final double maturity = exercise.getMaturity();
		final double[] exerciseTimes = exercise.getExerciseTimes();
		final double[] tauTimes = new double[exerciseTimes.length];

		for (int i = 0; i < exerciseTimes.length; i++) {
			tauTimes[i] = maturity - exerciseTimes[i];
		}

		java.util.Arrays.sort(tauTimes);
		return tauTimes;
	}

	/**
	 * Returns a refined time discretization that includes all Bermudan exercise
	 * dates
	 * in time-to-maturity coordinates.
	 *
	 * <p>
	 * European and American exercise return the base discretization unchanged.
	 * </p>
	 *
	 * @param baseTimeDiscretization The original time discretization in tau.
	 * @param exercise The exercise specification.
	 * @return A refined time discretization in tau.
	 */
	public static TimeDiscretization refineTimeDiscretization(
			final TimeDiscretization baseTimeDiscretization,
			final Exercise exercise) {

		if (exercise.isEuropean() || exercise.isAmerican()) {
			return baseTimeDiscretization;
		}

		final Set<Double> mergedTimes = new TreeSet<>();

		for (int i = 0; i < baseTimeDiscretization.getNumberOfTimes(); i++) {
			mergedTimes.add(baseTimeDiscretization.getTime(i));
		}

		for (final double tau : getExerciseTimesToMaturity(exercise)) {
			mergedTimes.add(tau);
		}

		final double[] refinedTimes = new double[mergedTimes.size()];
		int index = 0;
		for (final Double time : mergedTimes) {
			refinedTimes[index++] = time;
		}

		return new TimeDiscretizationFromArray(refinedTimes);
	}

	/**
	 * Returns true if exercise is allowed at the given time-to-maturity.
	 *
	 * @param tau The time-to-maturity.
	 * @param exercise The exercise specification.
	 * @return True if exercise is allowed.
	 */
	public static boolean isExerciseAllowedAtTimeToMaturity(final double tau, final Exercise exercise) {
		final double runningTime = exercise.getMaturity() - tau;
		return exercise.isExerciseAllowed(runningTime);
	}

	/**
	 * Returns true if the exercise policy should be treated as continuous on
	 * the FD grid.
	 *
	 * @param exercise The exercise specification.
	 * @return True if the policy is continuous.
	 */
	public static boolean isContinuousExercise(final Exercise exercise) {
		return exercise.isContinuousExercise();
	}

	/**
	 * Returns true if the given tau-grid time is an actual Bermudan exercise
	 * time.
	 *
	 * @param tau The time-to-maturity.
	 * @param exercise The exercise specification.
	 * @return True if tau corresponds to a Bermudan exercise date.
	 */
	public static boolean isDiscreteExerciseTime(final double tau, final Exercise exercise) {
		if (exercise.isAmerican()) {
			return false;
		}

		for (final double exerciseTau : getExerciseTimesToMaturity(exercise)) {
			if (Math.abs(exerciseTau - tau) < TIME_TOLERANCE) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Convenience method returning the exercise start time in tau-coordinates
	 * for American exercise.
	 *
	 * @param exercise The exercise specification.
	 * @return The corresponding tau value, or 0.0 if not applicable.
	 */
	public static double getAmericanExerciseStartTimeToMaturity(final Exercise exercise) {
		if (!(exercise instanceof AmericanExercise)) {
			return 0.0;
		}

		final AmericanExercise americanExercise = (AmericanExercise) exercise;
		return exercise.getMaturity() - americanExercise.getExerciseStartTime();
	}
}