LocalRiskMinimizingHedgePortfolio.java

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

import java.util.ArrayList;

import net.finmath.exception.CalculationException;
import net.finmath.montecarlo.RandomVariableFromDoubleArray;
import net.finmath.montecarlo.assetderivativevaluation.AssetModelMonteCarloSimulationModel;
import net.finmath.montecarlo.conditionalexpectation.MonteCarloConditionalExpectationRegression;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretization;
import net.finmath.time.TimeDiscretizationFromArray;

/**
 * This class implements a mean variance hedged portfolio of a given product (a hedge simulator).
 * The hedge is done using a given model, that is, the model generating the states and the model
 * used to calculate the hedge portfolio may be different!
 *
 * <br>
 * WARNING: If the model used for calculating the delta is "slow" (e.g., a Monte-Carlo simulation)
 * then the calculation might take very long.
 * <br>
 *
 * @author Christian Fries
 * @version 1.0
 */
public class LocalRiskMinimizingHedgePortfolio extends AbstractAssetMonteCarloProduct {

	// Model assumptions for the hedge
	private final AbstractAssetMonteCarloProduct productToHedge;
	private final AssetModelMonteCarloSimulationModel modelUsedForHedging;

	private final TimeDiscretization timeDiscretizationForRebalancing;

	private final int numberOfBins;

	/**
	 * Construction of a variance minimizing hedge portfolio.
	 *
	 * @param productToHedge The financial product for which the hedge portfolio should be constructed.
	 * @param modelUsedForHedging The model used for calculating the hedge rations (deltas). This may differ from the model passed to <code>getValue</code>.
	 * @param timeDiscretizationForRebalancing The times at which the portfolio is re-structured.
	 * @param numberOfBins The number of bins to use in the estimation of the conditional expectation.
	 */
	public LocalRiskMinimizingHedgePortfolio(final AbstractAssetMonteCarloProduct productToHedge,
			final AssetModelMonteCarloSimulationModel modelUsedForHedging,
			final TimeDiscretization timeDiscretizationForRebalancing,
			final int numberOfBins) {
		super();
		this.productToHedge = productToHedge;
		this.modelUsedForHedging = modelUsedForHedging;
		this.timeDiscretizationForRebalancing = timeDiscretizationForRebalancing;
		this.numberOfBins = numberOfBins;
	}

	@Override
	public RandomVariable getValue(final double evaluationTime, final AssetModelMonteCarloSimulationModel model) throws CalculationException {

		// Ask the model for its discretization
		final int timeIndexEvaluationTime	= model.getTimeIndex(evaluationTime);
		final int numberOfPath			= model.getNumberOfPaths();

		/*
		 *  Going forward in time we monitor the hedge portfolio on each path.
		 */

		/*
		 *  Initialize the portfolio to zero stocks and as much cash as the Black-Scholes Model predicts we need.
		 */
		final RandomVariable numeraireToday  = model.getNumeraire(0.0);
		final double valueOfOptionAccordingHedgeModel = productToHedge.getValue(modelUsedForHedging);

		// We store the composition of the hedge portfolio (depending on the path)
		RandomVariable amountOfNumeraireAsset		= numeraireToday.invert().mult(valueOfOptionAccordingHedgeModel);
		RandomVariable amountOfUderlyingAsset		= model.getRandomVariableForConstant(0.0);

		for(int timeIndex = 0; timeIndex<timeDiscretizationForRebalancing.getNumberOfTimes()-1; timeIndex++) {
			final double time		=	timeDiscretizationForRebalancing.getTime(timeIndex);
			final double timeNext	=	timeDiscretizationForRebalancing.getTime(timeIndex+1);

			if(time > evaluationTime) {
				break;
			}

			// Get value of underlying and numeraire assets
			final RandomVariable underlyingAtTime = modelUsedForHedging.getAssetValue(time,0);
			final RandomVariable numeraireAtTime  = modelUsedForHedging.getNumeraire(time);
			final RandomVariable underlyingAtTimeNext = modelUsedForHedging.getAssetValue(timeNext,0);
			final RandomVariable numeraireAtTimeNext  = modelUsedForHedging.getNumeraire(timeNext);

			final RandomVariable productAtTime		= productToHedge.getValue(time, modelUsedForHedging);
			final RandomVariable productAtTimeNext	= productToHedge.getValue(timeNext, modelUsedForHedging);

			final RandomVariable[] basisFunctionsEstimator = getBasisFunctions(modelUsedForHedging.getAssetValue(time,0));
			final RandomVariable[] basisFunctionsPredictor = getBasisFunctions(model.getAssetValue(time,0));

			final MonteCarloConditionalExpectationRegression condExpectationHedging	= new MonteCarloConditionalExpectationRegression(basisFunctionsEstimator, basisFunctionsEstimator);
			final MonteCarloConditionalExpectationRegression condExpectationValuation	= new MonteCarloConditionalExpectationRegression(basisFunctionsEstimator, basisFunctionsPredictor);

			final RandomVariable underlyingRebased = underlyingAtTimeNext.div(numeraireAtTimeNext);
			final RandomVariable underlyingRebasedExpected = condExpectationHedging.getConditionalExpectation(underlyingRebased);
			final RandomVariable underlyingRebasedMartingale = underlyingRebased.sub(underlyingRebasedExpected);

			final RandomVariable derivativeRebased = productAtTimeNext.div(numeraireAtTimeNext);
			final RandomVariable derivativeRebasedExpected = condExpectationHedging.getConditionalExpectation(derivativeRebased);
			final RandomVariable derivativeRebasedMartingale = derivativeRebased.sub(derivativeRebasedExpected);

			final RandomVariable derivativeTimesUnderlying = derivativeRebasedMartingale.mult(underlyingRebasedMartingale);
			final RandomVariable derivativeTimesUnderlyingExpected = condExpectationValuation.getConditionalExpectation(derivativeTimesUnderlying);

			final RandomVariable underlyingRabasedMartingaleSquared = underlyingRebasedMartingale.squared();
			final RandomVariable underlyingRabasedMartingaleSquaredExpected = condExpectationValuation.getConditionalExpectation(underlyingRabasedMartingaleSquared);

			final RandomVariable delta = derivativeTimesUnderlyingExpected.div(underlyingRabasedMartingaleSquaredExpected);

			final RandomVariable underlyingValue = model.getAssetValue(time,0);
			final RandomVariable numeraireValue  = model.getNumeraire(time);

			// Determine the delta hedge
			final RandomVariable newNumberOfStocks		= delta;
			final RandomVariable stocksToBuy				= newNumberOfStocks.sub(amountOfUderlyingAsset);

			// Ensure self financing
			final RandomVariable numeraireAssetsToBuy		= stocksToBuy.mult(underlyingValue).div(numeraireValue).mult(-1);
			final RandomVariable newNumberOfNumeraireAsset	= amountOfNumeraireAsset.add(numeraireAssetsToBuy);

			// Update portfolio
			amountOfNumeraireAsset	= newNumberOfNumeraireAsset;
			amountOfUderlyingAsset	= newNumberOfStocks;
		}

		/*
		 * At maturity, calculate the value of the replication portfolio
		 */

		// Get value of underlying and numeraire assets
		final RandomVariable underlyingAtEvaluationTime	= model.getAssetValue(evaluationTime,0);
		final RandomVariable numeraireAtEvaluationTime	= model.getNumeraire(evaluationTime);

		final RandomVariable portfolioValue = amountOfNumeraireAsset.mult(numeraireAtEvaluationTime).add(amountOfUderlyingAsset.mult(underlyingAtEvaluationTime));

		return portfolioValue;
	}

	/**
	 * Create basis functions for a binning.
	 *
	 * @param underlying
	 * @return
	 */
	private RandomVariable[] getBasisFunctions(final RandomVariable underlying) {
		final double min = underlying.getMin();
		final double max = underlying.getMax();

		final ArrayList<RandomVariable> basisFunctionList = new ArrayList<>();
		final double[] discretization = (new TimeDiscretizationFromArray(min, numberOfBins, (max-min)/numberOfBins)).getAsDoubleArray();
		for(final double discretizationStep : discretization) {
			final RandomVariable indicator = underlying.sub(discretizationStep).choose(new RandomVariableFromDoubleArray(1.0), new RandomVariableFromDoubleArray(0.0));
			basisFunctionList.add(indicator);
		}

		return basisFunctionList.toArray(new RandomVariable[0]);
	}
}