Swaption.java

package net.finmath.finitedifference.interestrate.products;

import java.util.Arrays;

import net.finmath.finitedifference.grids.SpaceTimeDiscretization;
import net.finmath.finitedifference.interestrate.models.FiniteDifferenceInterestRateModel;
import net.finmath.finitedifference.solvers.FDMThetaMethod1D;
import net.finmath.modelling.EuropeanExercise;
import net.finmath.modelling.Exercise;
import net.finmath.montecarlo.interestrate.products.BermudanSwaptionFromSwapSchedules.SwaptionType;
import net.finmath.time.Schedule;
import net.finmath.time.TimeDiscretization;

/**
 * Finite-difference valuation of a swaption under an interest-rate
 * finite-difference model.
 *
 * <p>
 * This class is designed to cover European, Bermudan, and American swaptions
 * through a single {@link Exercise} field.
 * </p>
 *
 * <p>
 * The product is represented as an option on the value of an underlying swap.
 * At an admissible exercise time {@code t}, the intrinsic value is
 * </p>
 *
 * <p>
 * <i>
 * max(V_swap(t,x), 0).
 * </i>
 * </p>
 *
 * <p>
 * The sign convention is determined by {@link SwaptionType}:
 * </p>
 * <ul>
 * <li>{@code PAYER}: intrinsic value is based on float leg minus fixed
 * leg,</li>
 * <li>{@code RECEIVER}: intrinsic value is based on fixed leg minus float
 * leg.</li>
 * </ul>
 *
 * <p>
 * The class supports:
 * </p>
 * <ul>
 * <li>a single underlying swap (European or American-style grid exercise),</li>
 *   <li>one underlying swap per exercise date (Bermudan style).</li>
 * </ul>
 *
 * <p>
 * Exercise handling is implemented through the interest-rate event-condition
 * mechanism. At each admissible exercise time {@code t},
 * </p>
 *
 * <p>
 * <i>
 * V(t^{-},x) = max(V(t^{+},x), intrinsic(t,x)).
 * </i>
 * </p>
 *
 * <p>
 * American exercise is approximated on the solver time grid and is currently
 * supported only with a single master underlying swap definition.
 * </p>
 *
 * <p>
 * Assumption:
 * the swap schedules supplied for a given exercise date should represent the
 * remaining underlying swap from that exercise date onward. In particular, this
 * class is intended for standard European/Bermudan swaption use cases where the
 * exercise dates and underlying remaining swaps are specified consistently.
 * </p>
 *
 * @author Alessandro Gnoatto
 */
public class Swaption implements FiniteDifferenceInterestRateProduct {

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

	/**
	 * The exercise.
	 */
	private final Exercise exercise;
	/**
	 * The explicit exercise dates.
	 */
	private final double[] explicitExerciseDates;

	/**
	 * The swaption type.
	 */
	private final SwaptionType swaptionType;
	/**
	 * The forward curve name.
	 */
	private final String forwardCurveName;

	/**
	 * The swap rates.
	 */
	private final double[] swapRates;
	/**
	 * The notionals.
	 */
	private final double[] notionals;
	/**
	 * The fix schedules.
	 */
	private final Schedule[] fixSchedules;
	/**
	 * The float schedules.
	 */
	private final Schedule[] floatSchedules;

	/**
	 * Creates a swaption.
	 *
	 * <p>
	 * If {@code explicitExerciseDates} is {@code null}, then
	 * </p>
	 * <ul>
	 *   <li>European exercise uses {@code exercise.getMaturity()},</li>
	 * <li>Bermudan exercise derives exercise dates from the first period start
	 * of
	 * the supplied schedules if more than one underlying swap is provided,</li>
	 *   <li>American exercise uses all solver time-grid points up to
	 * {@code exercise.getMaturity()} and requires exactly one underlying swap
	 *       definition.</li>
	 * </ul>
	 *
	 * @param exercise The exercise specification.
	 * @param explicitExerciseDates Optional explicit exercise dates.
	 * @param swaptionType The payer/receiver indicator.
	 * @param forwardCurveName The forwarding-curve name. May be {@code null}.
	 * @param swapRates The fixed rates of the underlying swaps.
	 * @param notionals The notionals of the underlying swaps. Must be positive.
	 * @param fixSchedules The fixed-leg schedules of the underlying swaps.
	 * @param floatSchedules The floating-leg schedules of the underlying swaps.
	 */
	public Swaption(
			final Exercise exercise,
			final double[] explicitExerciseDates,
			final SwaptionType swaptionType,
			final String forwardCurveName,
			final double[] swapRates,
			final double[] notionals,
			final Schedule[] fixSchedules,
			final Schedule[] floatSchedules) {

		if (exercise == null) {
			throw new IllegalArgumentException("exercise must not be null.");
		}
		if (swaptionType == null) {
			throw new IllegalArgumentException("swaptionType must not be null.");
		}
		if (swapRates == null || swapRates.length == 0) {
			throw new IllegalArgumentException("swapRates must contain at least one element.");
		}
		if (notionals == null || notionals.length != swapRates.length) {
			throw new IllegalArgumentException("notionals must have the same length as swapRates.");
		}
		if (fixSchedules == null || fixSchedules.length != swapRates.length) {
			throw new IllegalArgumentException("fixSchedules must have the same length as swapRates.");
		}
		if (floatSchedules == null || floatSchedules.length != swapRates.length) {
			throw new IllegalArgumentException("floatSchedules must have the same length as swapRates.");
		}

		this.exercise = exercise;
		this.explicitExerciseDates = explicitExerciseDates == null ? null : explicitExerciseDates.clone();
		this.swaptionType = swaptionType;
		this.forwardCurveName = forwardCurveName;
		this.swapRates = swapRates.clone();
		this.notionals = notionals.clone();
		this.fixSchedules = fixSchedules.clone();
		this.floatSchedules = floatSchedules.clone();

		validateInputs();
	}

	/**
	 * Creates a swaption without explicit exercise dates.
	 *
	 * @param exercise The exercise specification.
	 * @param swaptionType The payer/receiver indicator.
	 * @param forwardCurveName The forwarding-curve name. May be {@code null}.
	 * @param swapRates The fixed rates of the underlying swaps.
	 * @param notionals The notionals of the underlying swaps. Must be positive.
	 * @param fixSchedules The fixed-leg schedules of the underlying swaps.
	 * @param floatSchedules The floating-leg schedules of the underlying swaps.
	 */
	public Swaption(
			final Exercise exercise,
			final SwaptionType swaptionType,
			final String forwardCurveName,
			final double[] swapRates,
			final double[] notionals,
			final Schedule[] fixSchedules,
			final Schedule[] floatSchedules) {
		this(
				exercise,
				null,
				swaptionType,
				forwardCurveName,
				swapRates,
				notionals,
				fixSchedules,
				floatSchedules
				);
	}

	@Override
	public double[] getValue(
			final double evaluationTime,
			final FiniteDifferenceInterestRateModel model) {

		final ResolvedExerciseData resolvedExerciseData = getResolvedExerciseData(model);
		final double lastExerciseTime = resolvedExerciseData.getLastExerciseTime();

		final ResolvedSwaption resolvedProduct = new ResolvedSwaption(resolvedExerciseData);

		final FDMThetaMethod1D solver = new FDMThetaMethod1D(
				model,
				resolvedProduct,
				model.getSpaceTimeDiscretization(),
				new EuropeanExercise(lastExerciseTime)
				);

		return solver.getValue(
				evaluationTime,
				lastExerciseTime,
				buildZeroTerminalValues(model)
				);
	}

	@Override
	public double[][] getValues(final FiniteDifferenceInterestRateModel model) {

		final ResolvedExerciseData resolvedExerciseData = getResolvedExerciseData(model);
		final double lastExerciseTime = resolvedExerciseData.getLastExerciseTime();

		final ResolvedSwaption resolvedProduct = new ResolvedSwaption(resolvedExerciseData);

		final FDMThetaMethod1D solver = new FDMThetaMethod1D(
				model,
				resolvedProduct,
				model.getSpaceTimeDiscretization(),
				new EuropeanExercise(lastExerciseTime)
				);

		return solver.getValues(
				lastExerciseTime,
				buildZeroTerminalValues(model)
				);
	}

	@Override
	public double[] getEventTimes() {
		if (exercise.isAmerican()) {
			throw new UnsupportedOperationException(
					"American swaption exercise times depend on the model time grid. "
							+ "Use getValue(...) or getValues(...), which resolve the exercise times internally."
					);
		}

		return resolveDiscreteExerciseTimes();
	}

	/**
	 * Returns the exercise specification.
	 *
	 * @return The exercise specification.
	 */
	public Exercise getExercise() {
		return exercise;
	}

	/**
	 * Returns the explicit exercise dates, if any.
	 *
	 * @return The explicit exercise dates, or {@code null}.
	 */
	public double[] getExplicitExerciseDates() {
		return explicitExerciseDates == null ? null : explicitExerciseDates.clone();
	}

	/**
	 * Returns the swaption type.
	 *
	 * @return The swaption type.
	 */
	public SwaptionType getSwaptionType() {
		return swaptionType;
	}

	/**
	 * Returns the forwarding-curve name.
	 *
	 * @return The forwarding-curve name, possibly {@code null}.
	 */
	public String getForwardCurveName() {
		return forwardCurveName;
	}

	/**
	 * Returns the fixed rates of the underlying swaps.
	 *
	 * @return The fixed rates.
	 */
	public double[] getSwapRates() {
		return swapRates.clone();
	}

	/**
	 * Returns the notionals of the underlying swaps.
	 *
	 * @return The notionals.
	 */
	public double[] getNotionals() {
		return notionals.clone();
	}

	/**
	 * Returns the fixed-leg schedules.
	 *
	 * @return The fixed-leg schedules.
	 */
	public Schedule[] getFixSchedules() {
		return fixSchedules.clone();
	}

	/**
	 * Returns the floating-leg schedules.
	 *
	 * @return The floating-leg schedules.
	 */
	public Schedule[] getFloatSchedules() {
		return floatSchedules.clone();
	}

	/**
	 * Resolves the exercise data to be used numerically under the given model.
	 *
	 * <p>
	 * For American exercise, the exercise times depend on the model time grid.
	 * For European and Bermudan exercise, they are determined directly from the
	 * contractual product data.
	 * </p>
	 *
	 * @param model The finite-difference interest-rate model.
	 * @return The resolved exercise data.
	 */
	public ResolvedExerciseData getResolvedExerciseData(final FiniteDifferenceInterestRateModel model) {
		validateModel(model);

		if (exercise.isAmerican()) {
			return resolveAmericanExerciseData(model);
		}

		final double[] exerciseTimes = resolveDiscreteExerciseTimes();
		final int[] scheduleIndices = resolveDiscreteScheduleIndices(exerciseTimes);

		return new ResolvedExerciseData(exerciseTimes, scheduleIndices);
	}

	/**
	 * Returns the intrinsic value at the given time and state for the schedule
	 * indexed by {@code scheduleIndex}.
	 *
	 * @param time The running time.
	 * @param scheduleIndex The schedule index.
	 * @param stateVariable The state variable.
	 * @param model The finite-difference interest-rate model.
	 * @return The intrinsic value.
	 */
	public double getIntrinsicValue(
			final double time,
			final int scheduleIndex,
			final double stateVariable,
			final FiniteDifferenceInterestRateModel model) {
		return Math.max(getUnderlyingSwapValue(time, scheduleIndex, stateVariable, model), 0.0);
	}

	/**
	 * Returns the underlying swap value at the given time and state for the
	 * schedule indexed by {@code scheduleIndex}.
	 *
	 * @param time The running time.
	 * @param scheduleIndex The schedule index.
	 * @param stateVariable The state variable.
	 * @param model The finite-difference interest-rate model.
	 * @return The underlying swap value.
	 */
	public double getUnderlyingSwapValue(
			final double time,
			final int scheduleIndex,
			final double stateVariable,
			final FiniteDifferenceInterestRateModel model) {

		final double fixedLegValue = getFixedLegValue(time, scheduleIndex, stateVariable, model);
		final double floatingLegValue = getFloatingLegValue(time, scheduleIndex, stateVariable, model);

		switch (swaptionType) {
		case PAYER:
			return floatingLegValue - fixedLegValue;
		case RECEIVER:
			return fixedLegValue - floatingLegValue;
		default:
			throw new IllegalArgumentException("Unsupported swaption type: " + swaptionType);
		}
	}

	private void validateInputs() {
		for (int i = 0; i < notionals.length; i++) {
			if (notionals[i] <= 0.0) {
				throw new IllegalArgumentException("All notionals must be strictly positive.");
			}
		}

		if (explicitExerciseDates != null) {
			if (explicitExerciseDates.length == 0) {
				throw new IllegalArgumentException("explicitExerciseDates must not be empty.");
			}
			for (int i = 0; i < explicitExerciseDates.length; i++) {
				if (explicitExerciseDates[i] < 0.0) {
					throw new IllegalArgumentException("explicitExerciseDates must be non-negative.");
				}
				if (i > 0 && explicitExerciseDates[i] <= explicitExerciseDates[i - 1]) {
					throw new IllegalArgumentException("explicitExerciseDates must be strictly increasing.");
				}
			}
		}

		for (int i = 0; i < fixSchedules.length; i++) {
			validateSchedule(fixSchedules[i], "fixSchedules[" + i + "]");
			validateSchedule(floatSchedules[i], "floatSchedules[" + i + "]");
		}

		if (exercise.isAmerican() && fixSchedules.length != 1) {
			throw new IllegalArgumentException(
					"American exercise currently requires exactly one master underlying swap definition."
					);
		}
	}

	private void validateSchedule(final Schedule schedule, final String name) {
		if (schedule == null) {
			throw new IllegalArgumentException(name + " must not be null.");
		}
		if (schedule.getNumberOfPeriods() <= 0) {
			throw new IllegalArgumentException(name + " must contain at least one period.");
		}
		for (int periodIndex = 0; periodIndex < schedule.getNumberOfPeriods(); periodIndex++) {
			if (schedule.getFixing(periodIndex) < 0.0) {
				throw new IllegalArgumentException(name + " contains a negative fixing time.");
			}
			if (schedule.getPayment(periodIndex) <= schedule.getFixing(periodIndex)) {
				throw new IllegalArgumentException(
						name + " must satisfy payment > fixing for every period."
						);
			}
		}
	}

	private void validateModel(final FiniteDifferenceInterestRateModel model) {
		if (model == null) {
			throw new IllegalArgumentException("model must not be null.");
		}
		if (model.getSpaceTimeDiscretization().getNumberOfSpaceGrids() != 1) {
			throw new IllegalArgumentException(
					"Swaption currently supports only one-dimensional finite-difference interest-rate models."
					);
		}
	}

	private double[] buildZeroTerminalValues(final FiniteDifferenceInterestRateModel model) {
		final SpaceTimeDiscretization discretization = model.getSpaceTimeDiscretization();
		final double[] xGrid = discretization.getSpaceGrid(0).getGrid();
		return new double[xGrid.length];
	}

	private double[] resolveDiscreteExerciseTimes() {

		if (explicitExerciseDates != null) {
			return explicitExerciseDates.clone();
		}

		if (exercise.isEuropean()) {
			return new double[] {exercise.getMaturity() };
		}

		if (exercise.isBermudan()) {
			if (fixSchedules.length == 1) {
				return new double[] {exercise.getMaturity() };
			}

			final double[] exerciseTimes = new double[fixSchedules.length];
			for (int i = 0; i < exerciseTimes.length; i++) {
				exerciseTimes[i] = inferExerciseDateFromSchedules(i);
			}
			return exerciseTimes;
		}

		throw new IllegalArgumentException("Unsupported exercise type.");
	}

	private int[] resolveDiscreteScheduleIndices(final double[] exerciseTimes) {

		if (fixSchedules.length == 1) {
			final int[] scheduleIndices = new int[exerciseTimes.length];
			Arrays.fill(scheduleIndices, 0);
			return scheduleIndices;
		}

		if (fixSchedules.length != exerciseTimes.length) {
			throw new IllegalArgumentException(
					"The number of underlying swap definitions must be either 1 or equal to the number of exercise dates."
					);
		}

		final int[] scheduleIndices = new int[exerciseTimes.length];
		for (int i = 0; i < scheduleIndices.length; i++) {
			scheduleIndices[i] = i;
		}
		return scheduleIndices;
	}

	private ResolvedExerciseData resolveAmericanExerciseData(final FiniteDifferenceInterestRateModel model) {
		final TimeDiscretization timeDiscretization = model.getSpaceTimeDiscretization().getTimeDiscretization();
		final double exerciseMaturity = exercise.getMaturity();

		int count = 0;
		for (int i = 0; i < timeDiscretization.getNumberOfTimes(); i++) {
			if (timeDiscretization.getTime(i) <= exerciseMaturity + TIME_TOLERANCE) {
				count++;
			}
		}

		if (count == 0) {
			throw new IllegalArgumentException("No model time-grid point is admissible for American exercise.");
		}

		final double[] exerciseTimes = new double[count];
		final int[] scheduleIndices = new int[count];

		int index = 0;
		for (int i = 0; i < timeDiscretization.getNumberOfTimes(); i++) {
			final double time = timeDiscretization.getTime(i);
			if (time <= exerciseMaturity + TIME_TOLERANCE) {
				exerciseTimes[index] = time;
				scheduleIndices[index] = 0;
				index++;
			}
		}

		return new ResolvedExerciseData(exerciseTimes, scheduleIndices);
	}

	private double inferExerciseDateFromSchedules(final int scheduleIndex) {
		final Schedule fixSchedule = fixSchedules[scheduleIndex];
		final Schedule floatSchedule = floatSchedules[scheduleIndex];

		final double fixStart = fixSchedule.getPeriodStart(0);
		final double floatStart = floatSchedule.getPeriodStart(0);

		if (Math.abs(fixStart - floatStart) > TIME_TOLERANCE) {
			throw new IllegalArgumentException(
					"Cannot infer a unique exercise date from schedule pair "
							+ scheduleIndex + ": first fixed-leg start and first float-leg start differ."
					);
		}

		return fixStart;
	}

	private double getFixedLegValue(
			final double time,
			final int scheduleIndex,
			final double stateVariable,
			final FiniteDifferenceInterestRateModel model) {

		final Schedule fixedSchedule = fixSchedules[scheduleIndex];
		final double fixedRate = swapRates[scheduleIndex];
		final double notional = notionals[scheduleIndex];

		double value = 0.0;

		for (int periodIndex = 0; periodIndex < fixedSchedule.getNumberOfPeriods(); periodIndex++) {
			final double paymentTime = fixedSchedule.getPayment(periodIndex);

			if (paymentTime <= time + TIME_TOLERANCE) {
				continue;
			}

			final double accrualFactor = fixedSchedule.getPeriodLength(periodIndex);
			final double discountBond = model.getDiscountBond(time, paymentTime, stateVariable);

			value += notional * fixedRate * accrualFactor * discountBond;
		}

		return value;
	}

	private double getFloatingLegValue(
			final double time,
			final int scheduleIndex,
			final double stateVariable,
			final FiniteDifferenceInterestRateModel model) {

		final Schedule floatingSchedule = floatSchedules[scheduleIndex];
		final double notional = notionals[scheduleIndex];

		double value = 0.0;

		for (int periodIndex = 0; periodIndex < floatingSchedule.getNumberOfPeriods(); periodIndex++) {
			final double fixingTime = floatingSchedule.getFixing(periodIndex);
			final double paymentTime = floatingSchedule.getPayment(periodIndex);

			/*
			 * The intended use is that the schedule supplied for a given
			 * exercise
			 * date is already the remaining forward swap from that date onward.
			 * We therefore ignore already-started periods here.
			 */
			if ((fixingTime < time - TIME_TOLERANCE) || (paymentTime <= time + TIME_TOLERANCE)) {
				continue;
			}

			final double accrualFactor = floatingSchedule.getPeriodLength(periodIndex);
			final double forwardRate = model.getForwardRate(
					forwardCurveName,
					time,
					fixingTime,
					paymentTime,
					stateVariable
					);
			final double discountBond = model.getDiscountBond(time, paymentTime, stateVariable);

			value += notional * forwardRate * accrualFactor * discountBond;
		}

		return value;
	}

	@Override
	public String toString() {
		return "Swaption [exercise=" + exercise
				+ ", explicitExerciseDates=" + Arrays.toString(explicitExerciseDates)
				+ ", swaptionType=" + swaptionType
				+ ", forwardCurveName=" + forwardCurveName
				+ ", swapRates=" + Arrays.toString(swapRates)
				+ ", notionals=" + Arrays.toString(notionals)
				+ ", fixSchedules=" + Arrays.toString(fixSchedules)
				+ ", floatSchedules=" + Arrays.toString(floatSchedules)
				+ "]";
	}

	/**
	 * Container for resolved exercise information.
	 *
	 * @author Alessandro Gnoatto
	 */
	public static final class ResolvedExerciseData {

		/**
		 * The exercise times.
		 */
		private final double[] exerciseTimes;
		/**
		 * The schedule indices.
		 */
		private final int[] scheduleIndices;

		private ResolvedExerciseData(
				final double[] exerciseTimes,
				final int[] scheduleIndices) {
			this.exerciseTimes = exerciseTimes.clone();
			this.scheduleIndices = scheduleIndices.clone();
		}

		/**
		 * Returns the resolved exercise times.
		 *
		 * @return The resolved exercise times.
		 */
		public double[] getExerciseTimes() {
			return exerciseTimes.clone();
		}

		/**
		 * Returns the resolved schedule indices.
		 *
		 * @return The resolved schedule indices.
		 */
		public int[] getScheduleIndices() {
			return scheduleIndices.clone();
		}

		/**
		 * Returns the number of resolved exercise opportunities.
		 *
		 * @return The number of resolved exercise opportunities.
		 */
		public int getNumberOfExerciseTimes() {
			return exerciseTimes.length;
		}

		/**
		 * Returns the exercise time at the given index.
		 *
		 * @param index The index.
		 * @return The exercise time.
		 */
		public double getExerciseTime(final int index) {
			return exerciseTimes[index];
		}

		/**
		 * Returns the schedule index at the given index.
		 *
		 * @param index The index.
		 * @return The schedule index.
		 */
		public int getScheduleIndex(final int index) {
			return scheduleIndices[index];
		}

		/**
		 * Returns the last exercise time.
		 *
		 * @return The last exercise time.
		 */
		public double getLastExerciseTime() {
			return exerciseTimes[exerciseTimes.length - 1];
		}

		/**
		 * Returns the resolved exercise index for the given time.
		 *
		 * @param time The running time.
		 * @return The resolved exercise index.
		 */
		public int findExerciseIndex(final double time) {
			for (int i = 0; i < exerciseTimes.length; i++) {
				if (Math.abs(exerciseTimes[i] - time) <= TIME_TOLERANCE) {
					return i;
				}
			}

			throw new IllegalArgumentException("No exercise index found for time " + time + ".");
		}
	}

	/**
	 * Internal resolved product with model-resolved exercise times.
	 *
	 * @author Alessandro Gnoatto
	 */
	public final class ResolvedSwaption implements FiniteDifferenceInterestRateProduct {

		/**
		 * The resolved exercise data.
		 */
		private final ResolvedExerciseData resolvedExerciseData;

		private ResolvedSwaption(final ResolvedExerciseData resolvedExerciseData) {
			this.resolvedExerciseData = resolvedExerciseData;
		}

		/**
		 * Returns the original outer swaption.
		 *
		 * @return The original swaption.
		 */
		public Swaption getOriginalSwaption() {
			return Swaption.this;
		}

		@Override
		public double[] getEventTimes() {
			return resolvedExerciseData.getExerciseTimes();
		}

		@Override
		public double[] applyEventCondition(
				final double time,
				final double[] valuesAfterEvent,
				final FiniteDifferenceInterestRateModel model) {

			if (valuesAfterEvent == null) {
				throw new IllegalArgumentException("valuesAfterEvent must not be null.");
			}

			final int exerciseIndex = resolvedExerciseData.findExerciseIndex(time);
			final int scheduleIndex = resolvedExerciseData.getScheduleIndex(exerciseIndex);

			final double[] xGrid = model.getSpaceTimeDiscretization().getSpaceGrid(0).getGrid();

			if (valuesAfterEvent.length != xGrid.length) {
				throw new IllegalArgumentException(
						"The value vector length does not match the spatial grid length.");
			}

			final double[] valuesBeforeEvent = valuesAfterEvent.clone();

			for (int i = 0; i < xGrid.length; i++) {
				final double intrinsicValue = getIntrinsicValue(
						time,
						scheduleIndex,
						xGrid[i],
						model
						);
				valuesBeforeEvent[i] = Math.max(valuesBeforeEvent[i], intrinsicValue);
			}

			return valuesBeforeEvent;
		}

		@Override
		public double[] getValue(
				final double evaluationTime,
				final FiniteDifferenceInterestRateModel model) {
			return Swaption.this.getValue(evaluationTime, model);
		}

		@Override
		public double[][] getValues(final FiniteDifferenceInterestRateModel model) {
			return Swaption.this.getValues(model);
		}
	}
}