CoxRossRubinsteinModel.java

package net.finmath.tree.assetderivativevaluation.models;

import net.finmath.montecarlo.RandomVariableFromDoubleArray;
import net.finmath.stochastic.RandomVariable;
import net.finmath.tree.assetderivativevaluation.AbstractRecombiningTreeModel;

/**
 * This class represents the approximation of a Black-Scholes model via the Cox Ross Rubinstein  model.
 * It extends a recombining tree model. The implemented method computes the up and down factors
 * @author Andrea Mazzon
 * @version 1.0
 */

public class CoxRossRubinsteinModel extends AbstractRecombiningTreeModel {

	/** Specific CRR parameters */
	private final double u;
	private final double d;
	private final double q;

	/**
	 * It constructs an object which represents the approximation of a Black-Scholes model via the Cox Ross
	 * Rubinstein model.
	 *
	 * @param initialPrice, the initial price of the asset modeled by the process
	 * @param riskFreeRate, the number r such that the value of a risk-free bond at time T is e^(rT)
	 * @param volatility, the log-volatility of the Black-Scholes model
	 * @param lastTime, the last time T in the time discretization {@code 0=t_0<t_1<..<t_n=T}
	 * @param timeStep, the length t_k-t_{k-1} of the equally spaced time steps that we take for the approximating
	 * time discretization {@code 0=t_0<t_1<..<t_n=T}
	 */
	public CoxRossRubinsteinModel(double initialPrice, double riskFreeRate, double volatility, double lastTime, double timeStep) {
		super(initialPrice, riskFreeRate, volatility, lastTime, timeStep);
		final double dt = getTimeStep();
		final double sigma = getVolatility();
		final double r = getRiskFreeRate();
		this.u = Math.exp(sigma * Math.sqrt(dt));
		this.d = 1.0 / u;
		final double growth = Math.exp(r * dt);
		this.q = (growth - d) / (u - d);
	}

	/**
	 * It constructs an object which represents the approximation of a Black-Scholes model via the Cox Ross
	 * Rubinstein model.
	 *
	 * @param initialPrice, the initial price of the asset modeled by the process
	 * @param riskFreeRate, the number r such that the value of a risk-free bond at time T is e^(rT)
	 * @param volatility, the log-volatility of the Black-Scholes model
	 * @param lastTime, the last time T in the time discretization {@code 0=t_0<t_1<..<t_n=T}
	 * @param numberOfTimes the number of equally spaced time steps
	 */
	public CoxRossRubinsteinModel(double initialPrice, double riskFreeRate, double volatility, double lastTime, int numberOfTimes) {
		super(initialPrice, riskFreeRate, volatility, lastTime, numberOfTimes);

		final double dt = getTimeStep();
		final double sigma = getVolatility();
		final double r = getRiskFreeRate();

		this.u = Math.exp(sigma * Math.sqrt(dt));
		this.d = 1.0 / u;
		final double growth = Math.exp(r * dt);
		this.q = (growth - d) / (u - d);
	}

	/** Abstract hook to implement in the underlying classes
	 * Binomial: at k level there are k+1 states
	 * @param  k level of depth
	 * */
	@Override
	public int statesAt(int k) {
		return k + 1;
	}

	/** Build S_k[i] with agreement i = #down => S0 * u^(k-i) * d^i.
	 * @param  k level of depth */
	@Override
	protected RandomVariable buildSpotLevel(int k) {
		final double[] s = new double[statesAt(k)];
		final double s0 = getInitialPrice();
		int down = 0;
		int up = 0;
		for (int i = 0; i <= k; i++) {
			up = k - i;
			down = i;
			s[i] = s0 * Math.pow(u, up) * Math.pow(d, down);
		}
		return new RandomVariableFromDoubleArray(k*getTimeStep(),s);
	}

	/**
	 * Discounted conditional expectation : V_k[i] = df() * ( q * V_{k+1}[i] + (1-q) * V_{k+1}[i+1] ).
	 */
	@Override
	protected RandomVariable conditionalExpectation(RandomVariable vNext, int k) {
		final double[] next = vNext.getRealizations();
		final double[] vK = new double[statesAt(k)];
		final double disc = getOneStepDiscountFactor(k);

		for (int i = 0; i <= k; i++) {
			vK[i] = disc * (q * next[i] + (1.0 - q) * next[i + 1]);
		}
		return new RandomVariableFromDoubleArray(k * getTimeStep(), vK);
	}

	@Override
	public int getNumberOfBranches(int timeIndex, int stateIndex) {
		return 2;
	}

	@Override
	public double getTransitionProbability(int timeIndex, int stateIndex, int branchIndex) {
		// Convention: 0 = up, 1 = down
		switch(branchIndex) {
		case 0: return q;
		case 1: return 1.0 - q;
		default: throw new IllegalArgumentException("Invalid branchIndex " + branchIndex + " for binomial model.");
		}
	}

	@Override
	public int[] getChildStateIndexShift() {
		// Convention: childIndex = parentIndex + shift[branchIndex]
		// For binomial recombining trees: up keeps index, down increases index by 1.
		return new int[] { 0, 1 };
	}

	/** Getter */
	public double getU() { return u; }
	public double getD() { return d; }
	public double getQ() { return q; }
}