AbstractLIBORMarketModel.java

/*
 * (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
 *
 * Created on 04.02.2004
 */
package net.finmath.montecarlo.interestrate.simple;

import java.time.LocalDateTime;

import net.finmath.montecarlo.BrownianMotion;
import net.finmath.montecarlo.RandomVariableFromDoubleArray;
import net.finmath.montecarlo.interestrate.LIBORModelMonteCarloSimulationModel;
import net.finmath.montecarlo.templatemethoddesign.LogNormalProcess;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretization;

/**
 * This class represents an abstract base class for a LIBOR Market Model.
 * Derive from this class and implement initial value, volatility and correlation (via factor loadings).
 *
 * A more flexible / complex version of the model can be created by using {@link net.finmath.montecarlo.interestrate.models.LIBORMarketModelFromCovarianceModel}
 * as an input to an {@link net.finmath.montecarlo.process.EulerSchemeFromProcessModel}.
 *
 * @author Christian Fries
 * @version 0.6
 * @since finmath-lib 4.1.0
 */
public abstract class AbstractLIBORMarketModel extends LogNormalProcess implements LIBORModelMonteCarloSimulationModel {

	// Discretization of the yield curve into LIBORs
	private final TimeDiscretization liborPeriodDiscretization;

	/**
	 * @param liborPeriodDiscretization2 The tenor
	 * @param brownianMotionLazyInit The Brownian motion (includes the specification of time discretization, number of factors, and number of paths.
	 */
	public AbstractLIBORMarketModel(
			final TimeDiscretization	liborPeriodDiscretization2,
			final BrownianMotion		brownianMotionLazyInit) {
		super(liborPeriodDiscretization2.getNumberOfTimeSteps(), brownianMotionLazyInit);
		liborPeriodDiscretization = liborPeriodDiscretization2;
	}

	//	@Override
	@Override
	public LocalDateTime getReferenceDate() {
		throw new UnsupportedOperationException("This model does not provide a reference date. Reference dates will be mandatory in a future version.");
	}

	@Override
	public abstract Object getCloneWithModifiedSeed(int seed);

	/**
	 * @param timeIndex The time index at which the numeraire is requested.
	 * @return The numeraire at the specified time as <code>RandomVariableFromDoubleArray</code>
	 */
	public abstract RandomVariable getNumeraire(int timeIndex);

	/**
	 * @param time The time at which the numeraire is requested.
	 * @return The numeraire at the specified time as <code>RandomVariableFromDoubleArray</code>
	 */
	@Override
	public RandomVariable getNumeraire(final double time) {
		return getNumeraire(getTimeIndex(time));
	}

	@Override
	public RandomVariable getLIBOR(final int timeIndex, final int liborIndex)
	{
		// This method is just a psynonym - call getProcessValue of super class
		return getProcessValue(timeIndex, liborIndex);
	}

	/**
	 * This method returns the vector of LIBORs at a certain time index.
	 *
	 * @param timeIndex Time index at which the process should be observed
	 * @return The process realizations
	 */
	@Override
	public RandomVariable[] getLIBORs(final int timeIndex)
	{
		final RandomVariable[] randomVariableVector = new RandomVariable[getNumberOfComponents()];
		for(int componentIndex=0; componentIndex<getNumberOfComponents(); componentIndex++) {
			randomVariableVector[componentIndex] = getLIBOR(timeIndex, componentIndex);
		}

		return randomVariableVector;
	}

	/**
	 * @param time			Simulation time
	 * @param periodStart	Start time of period
	 * @param periodEnd		End time of period
	 * @return				The LIBOR rate as seen on simulation time for the specified period
	 */
	@Override
	public RandomVariable getForwardRate(final double time, final double periodStart, final double periodEnd)
	{
		final int periodStartIndex	= getLiborPeriodIndex(periodStart);
		final int periodEndIndex		= getLiborPeriodIndex(periodEnd);

		final int timeIndex			= getTimeIndex(time);

		// If this is a model primitive then return it
		if(periodStartIndex+1==periodEndIndex) {
			return getProcessValue(timeIndex, periodStartIndex);
		}

		// The requeste LIBOR is not a model primitive. We need to calculate it (slow!)
		final double[] libor = new double[getNumberOfPaths()];
		java.util.Arrays.fill(libor,1.0);

		// Calculate the value of the forward bond
		for(int periodIndex = periodStartIndex; periodIndex<periodEndIndex; periodIndex++)
		{
			final double subPeriodLength = getLiborPeriod(periodIndex+1) - getLiborPeriod(periodIndex);
			final RandomVariable liborOverSubPeriod = getLIBOR(timeIndex, periodIndex);

			for(int path = 0; path<getNumberOfPaths(); path++) {
				libor[path] *= (1 + liborOverSubPeriod.get(path) * subPeriodLength);
			}
		}

		// Calculate the libor
		for(int path = 0; path<getNumberOfPaths(); path++) {
			libor[path] -= 1;
			libor[path] /= (periodEnd - periodStart);
		}

		return new RandomVariableFromDoubleArray(time,libor);
	}

	@Override
	public RandomVariable getMonteCarloWeights(final double time) {
		return getMonteCarloWeights(getTimeIndex(time));
	}

	/**
	 * @return The number of LIBORs in the LIBOR discretization
	 */
	@Override
	public int getNumberOfLibors()
	{
		// This is just a synonym to number of components
		return getNumberOfComponents();
	}

	/**
	 * @return The index corresponding to a given time (interpretation is start of period)
	 */
	@Override
	public double getLiborPeriod(final int timeIndex) {
		return liborPeriodDiscretization.getTime(timeIndex);
	}

	/**
	 * Same as java.util.Arrays.binarySearch(liborPeriodDiscretization,time). Will return a negative value if the time is not found, but then -index-1 corresponds to the index of the smallest time greater than the given one.
	 *
	 * @return The index corresponding to a given time (interpretation is start of period)
	 */
	@Override
	public int getLiborPeriodIndex(final double time) {
		return liborPeriodDiscretization.getTimeIndex(time);
	}

	@Override
	public TimeDiscretization getLiborPeriodDiscretization() {
		return liborPeriodDiscretization;
	}
}