/* * (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de. * * Created on 20.01.2004 */ package net.finmath.montecarlo.assetderivativevaluation; import java.util.HashMap; import java.util.Map; import net.finmath.compatibility.java.util.function.DoubleUnaryOperator; import net.finmath.compatibility.java.util.function.IntFunction; import net.finmath.exception.CalculationException; import net.finmath.functions.NormalDistribution; import net.finmath.functions.PoissonDistribution; import net.finmath.montecarlo.IndependentIncrements; import net.finmath.montecarlo.IndependentIncrementsInterface; import net.finmath.montecarlo.process.AbstractProcess; import net.finmath.montecarlo.process.ProcessEulerScheme; import net.finmath.stochastic.RandomVariableInterface; import net.finmath.time.TimeDiscretizationInterface; /** * This class glues together a <code>MertonModel</code> and a Monte-Carlo implementation of a <code>AbstractProcess</code>, namely <code>ProcessEulerScheme</code>, * and forms a Monte-Carlo implementation of the Merton model by implementing <code>AssetModelMonteCarloSimulationInterface</code>. * * The model is * \[ * dS = \mu S dt + \sigma S dW + S dJ, \quad S(0) = S_{0}, * \] * \[ * dN = r N dt, \quad N(0) = N_{0}, * \] * where \( W \) is Brownian motion and \( J \) is a jump process (compound Poisson process). * * The process \( J \) is given by \( J(t) = \sum_{i=1}^{N(t)} (Y_{i}-1) \), where * \( \log(Y_{i}) \) are i.i.d. normals with mean \( a - \frac{1}{2} b^{2} \) and standard deviation \( b \). * Here \( a \) is the jump size mean and \( b \) is the jump size std. dev. * * For details on the construction of the model see {@link net.finmath.montecarlo.assetderivativevaluation.MertonModel}. * * @author Christian Fries * @see net.finmath.montecarlo.assetderivativevaluation.MertonModel * @see net.finmath.montecarlo.process.AbstractProcessInterface The interface for numerical schemes. * @see net.finmath.montecarlo.model.AbstractModelInterface The interface for models provinding parameters to numerical schemes. */ public class MonteCarloMertonModel implements AssetModelMonteCarloSimulationInterface { private final MertonModel model; private final double initialValue; private final int seed; /** * Create a Monte-Carlo simulation using given time discretization and given parameters. * * @param timeDiscretization The time discretization. * @param numberOfPaths The number of Monte-Carlo path to be used. * @param seed The seed used for the random number generator. * @param initialValue Spot value. * @param riskFreeRate The risk free rate. * @param volatility The log volatility. * @param jumpIntensity The intensity parameter lambda of the compound Poisson process. * @param jumpSizeMean The mean jump size of the normal distributes jump sizes of the compound Poisson process. * @param jumpSizeStDev The standard deviation of the normal distributes jump sizes of the compound Poisson process. */ public MonteCarloMertonModel( final TimeDiscretizationInterface timeDiscretization, final int numberOfPaths, final int seed, final double initialValue, final double riskFreeRate, final double volatility, final double jumpIntensity, final double jumpSizeMean, final double jumpSizeStDev ) { super(); this.initialValue = initialValue; this.seed = seed; // Create the model model = new MertonModel(initialValue, riskFreeRate, volatility, jumpIntensity, jumpSizeMean, jumpSizeStDev); /* * Define the ICDFs based on index (i,j) = (time, factor) index. * This is much more elegant using Java 8 lambda expressions. */ IntFunction<IntFunction<DoubleUnaryOperator>> inverseCumulativeDistributionFunctions = new IntFunction<IntFunction<DoubleUnaryOperator>>() { public IntFunction<DoubleUnaryOperator> apply(final int i) { return new IntFunction<DoubleUnaryOperator>() { public DoubleUnaryOperator apply(final int j) { if(j==0) { // The Brownian increment final double sqrtOfTimeStep = Math.sqrt(timeDiscretization.getTimeStep(i)); return new DoubleUnaryOperator() { public double applyAsDouble(double x) { return NormalDistribution.inverseCumulativeDistribution(x)*sqrtOfTimeStep; } }; } else if(j==1) { // The random jump size return new DoubleUnaryOperator() { public double applyAsDouble(double x) { return NormalDistribution.inverseCumulativeDistribution(x); } }; } else if(j==2) { // The jump increment final double timeStep = timeDiscretization.getTimeStep(i); final PoissonDistribution poissonDistribution = new PoissonDistribution(jumpIntensity*timeStep); return new DoubleUnaryOperator() { public double applyAsDouble(double x) { return poissonDistribution.inverseCumulativeDistribution(x); } }; } else { return null; } } }; }; }; IndependentIncrementsInterface icrements = new IndependentIncrements(timeDiscretization, 3, numberOfPaths, seed, inverseCumulativeDistributionFunctions ) { private static final long serialVersionUID = -7858107751226404629L; @Override public RandomVariableInterface getIncrement(int timeIndex, int factor) { if(factor == 1) { RandomVariableInterface Z = super.getIncrement(timeIndex, 1); RandomVariableInterface N = super.getIncrement(timeIndex, 2); return Z.mult(N.sqrt()); } else { return super.getIncrement(timeIndex, factor); } } }; // Create a corresponding MC process AbstractProcess process = new ProcessEulerScheme(icrements); // Link model and process for delegation process.setModel(model); model.setProcess(process); } @Override public RandomVariableInterface getAssetValue(double time, int assetIndex) throws CalculationException { return getAssetValue(getTimeIndex(time), assetIndex); } @Override public RandomVariableInterface getAssetValue(int timeIndex, int assetIndex) throws CalculationException { return model.getProcess().getProcessValue(timeIndex, assetIndex); } @Override public RandomVariableInterface getNumeraire(int timeIndex) throws CalculationException { double time = getTime(timeIndex); return model.getNumeraire(time); } @Override public RandomVariableInterface getNumeraire(double time) throws CalculationException { return model.getNumeraire(time); } @Override public RandomVariableInterface getMonteCarloWeights(double time) throws CalculationException { return getMonteCarloWeights(getTimeIndex(time)); } /* (non-Javadoc) * @see net.finmath.montecarlo.assetderivativevaluation.AssetModelMonteCarloSimulationInterface#getNumberOfAssets() */ @Override public int getNumberOfAssets() { return 1; } @Override public AssetModelMonteCarloSimulationInterface getCloneWithModifiedData(Map<String, Object> dataModified) { /* * Determine the new model parameters from the provided parameter map. */ double newInitialTime = dataModified.get("initialTime") != null ? ((Number)dataModified.get("initialTime")).doubleValue() : getTime(0); double newInitialValue = dataModified.get("initialValue") != null ? ((Number)dataModified.get("initialValue")).doubleValue() : initialValue; double newRiskFreeRate = dataModified.get("riskFreeRate") != null ? ((Number)dataModified.get("riskFreeRate")).doubleValue() : model.getRiskFreeRate(); double newVolatility = dataModified.get("volatility") != null ? ((Number)dataModified.get("volatility")).doubleValue() : model.getVolatility(); double newJumpIntensity = dataModified.get("jumpIntensity") != null ? ((Number)dataModified.get("jumpIntensity")).doubleValue() : model.getJumpIntensity(); double newJumpSizeMean = dataModified.get("jumpSizeMean") != null ? ((Number)dataModified.get("jumpSizeMean")).doubleValue() : model.getVolatility(); double newJumpSizeStdDev = dataModified.get("jumpSizeStdDev") != null ? ((Number)dataModified.get("jumpSizeStdDev")).doubleValue() : model.getVolatility(); int newSeed = dataModified.get("seed") != null ? ((Number)dataModified.get("seed")).intValue() : seed; return new MonteCarloMertonModel(model.getProcess().getTimeDiscretization().getTimeShiftedTimeDiscretization(newInitialTime-getTime(0)), model.getProcess().getNumberOfPaths(), newSeed, newInitialValue, newRiskFreeRate, newVolatility, newJumpIntensity, newJumpSizeMean, newJumpSizeStdDev); } @Override public AssetModelMonteCarloSimulationInterface getCloneWithModifiedSeed(int seed) { Map<String, Object> dataModified = new HashMap<String, Object>(); dataModified.put("seed", new Integer(seed)); return getCloneWithModifiedData(dataModified); } @Override public int getNumberOfPaths() { return model.getProcess().getNumberOfPaths(); } @Override public TimeDiscretizationInterface getTimeDiscretization() { return model.getProcess().getTimeDiscretization(); } @Override public double getTime(int timeIndex) { return model.getProcess().getTime(timeIndex); } @Override public int getTimeIndex(double time) { return model.getProcess().getTimeIndex(time); } @Override public RandomVariableInterface getRandomVariableForConstant(double value) { return model.getProcess().getStochasticDriver().getRandomVariableForConstant(value); } @Override public RandomVariableInterface getMonteCarloWeights(int timeIndex) throws CalculationException { return model.getProcess().getMonteCarloWeights(timeIndex); } }