/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de.
*
* Created on 09.06.2014
*/
package net.finmath.montecarlo.assetderivativevaluation;
import java.util.Arrays;
import java.util.Map;
import net.finmath.exception.CalculationException;
import net.finmath.functions.LinearAlgebra;
import net.finmath.montecarlo.BrownianMotion;
import net.finmath.montecarlo.BrownianMotionInterface;
import net.finmath.montecarlo.model.AbstractModel;
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>BlackScholeModel</code> and a Monte-Carlo implementation of a <code>AbstractProcess</code>
* and forms a Monte-Carlo implementation of the Black-Scholes Model by implementing <code>AssetModelMonteCarloSimulationInterface</code>.
*
* The model is
* \[
* dS_{i} = r S_{i} dt + \sigma_{i} S_{i} dW_{i}, \quad S_{i}(0) = S_{i,0},
* \]
* \[
* dN = r N dt, \quad N(0) = N_{0},
* \]
* \[
* dW_{i} dW_{j} = \rho_{i,j} dt,
* \]
*
* The class provides the model of \( S_{i} \) to an <code>{@link net.finmath.montecarlo.process.AbstractProcessInterface}</code> via the specification of
* \( f = exp \), \( \mu_{i} = r - \frac{1}{2} \sigma_{i}^2 \), \( \lambda_{i,j} = \sigma_{i} g_{i,j} \), i.e.,
* of the SDE
* \[
* dX_{i} = \mu_{i} dt + \lambda_{i,j} dW, \quad X_{i}(0) = \log(S_{i,0}),
* \]
* with \( S = f(X) \). See {@link net.finmath.montecarlo.process.AbstractProcessInterface} for the notation.
*
* @author Christian Fries
* @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 MonteCarloMultiAssetBlackScholesModel extends AbstractModel implements AssetModelMonteCarloSimulationInterface {
private final double[] initialValues;
private final double riskFreeRate; // Actually the same as the drift (which is not stochastic)
private final double[] volatilities;
private final double[][] factorLoadings;
private static final int seed = 3141;
private final RandomVariableInterface[] initialStates;
private final RandomVariableInterface[] drift;
private final RandomVariableInterface[][] factorLoadingOnPaths;
/**
* Create a Monte-Carlo simulation using given time discretization.
*
* @param brownianMotion The Brownian motion to be used for the numerical scheme.
* @param initialValues Spot values.
* @param riskFreeRate The risk free rate.
* @param volatilities The log volatilities.
* @param correlations A correlation matrix.
*/
public MonteCarloMultiAssetBlackScholesModel(
BrownianMotionInterface brownianMotion,
double[] initialValues,
double riskFreeRate,
double[] volatilities,
double[][] correlations
) {
super();
// Create a corresponding MC process
AbstractProcess process = new ProcessEulerScheme(brownianMotion);
this.initialValues = initialValues;
this.riskFreeRate = riskFreeRate;
this.volatilities = volatilities;
this.factorLoadings = LinearAlgebra.getFactorMatrix(correlations, correlations.length);
/*
* The interface definition requires that we provide the initial value, the drift and the volatility in terms of random variables.
* We construct the corresponding random variables here and will return (immutable) references to them.
*
* Since the underlying process is configured to simulate log(S),
* the initial value and the drift are transformed accordingly.
*
*/
initialStates = new RandomVariableInterface[getNumberOfComponents()];
drift = new RandomVariableInterface[getNumberOfComponents()];
factorLoadingOnPaths = new RandomVariableInterface[getNumberOfComponents()][];
for(int underlyingIndex = 0; underlyingIndex<initialValues.length; underlyingIndex++) {
this.initialStates[underlyingIndex] = process.getBrownianMotion().getRandomVariableForConstant(Math.log(initialValues[underlyingIndex]));
this.drift[underlyingIndex] = process.getBrownianMotion().getRandomVariableForConstant(riskFreeRate - volatilities[underlyingIndex] * volatilities[underlyingIndex] / 2.0);
this.factorLoadingOnPaths[underlyingIndex] = new RandomVariableInterface[process.getNumberOfFactors()];
for(int factorIndex = 0; factorIndex<process.getNumberOfFactors(); factorIndex++) {
this.factorLoadingOnPaths[underlyingIndex][factorIndex] = process.getBrownianMotion().getRandomVariableForConstant(volatilities[underlyingIndex] * factorLoadings[underlyingIndex][factorIndex]);
}
}
// Link model and process for delegation
process.setModel(this);
this.setProcess(process);
}
/**
* Create a Monte-Carlo simulation using given time discretization.
*
* @param timeDiscretization The time discretization.
* @param numberOfPaths The number of Monte-Carlo path to be used.
* @param initialValues Spot values.
* @param riskFreeRate The risk free rate.
* @param volatilities The log volatilities.
* @param correlations A correlation matrix.
*/
public MonteCarloMultiAssetBlackScholesModel(
TimeDiscretizationInterface timeDiscretization,
int numberOfPaths,
double[] initialValues,
double riskFreeRate,
double[] volatilities,
double[][] correlations
) {
this(new BrownianMotion(timeDiscretization, initialValues.length /* numberOfFactors */, numberOfPaths, seed), initialValues, riskFreeRate, volatilities, correlations);
}
@Override
public RandomVariableInterface[] getInitialState() {
return initialStates;
}
@Override
public RandomVariableInterface[] getDrift(int timeIndex, RandomVariableInterface[] realizationAtTimeIndex, RandomVariableInterface[] realizationPredictor) {
return drift;
}
@Override
public RandomVariableInterface[] getFactorLoading(int timeIndex, int component, RandomVariableInterface[] realizationAtTimeIndex) {
return factorLoadingOnPaths[component];
}
@Override
public RandomVariableInterface applyStateSpaceTransform(int componentIndex, RandomVariableInterface randomVariable) {
return randomVariable.exp();
}
@Override
public RandomVariableInterface getAssetValue(double time, int assetIndex) throws CalculationException {
int timeIndex = getTimeIndex(time);
if(timeIndex < 0) timeIndex = -timeIndex-1;
return getAssetValue(timeIndex, assetIndex);
}
@Override
public RandomVariableInterface getAssetValue(int timeIndex, int assetIndex) throws CalculationException {
return getProcessValue(timeIndex, assetIndex);
}
@Override
public RandomVariableInterface getMonteCarloWeights(double time) throws CalculationException {
return getMonteCarloWeights(getTimeIndex(time));
}
@Override
public RandomVariableInterface getNumeraire(int timeIndex) {
double time = getTime(timeIndex);
return getNumeraire(time);
}
@Override
public RandomVariableInterface getNumeraire(double time) {
double numeraireValue = Math.exp(riskFreeRate * time);
return getRandomVariableForConstant(numeraireValue);
}
@Override
public RandomVariableInterface getRandomVariableForConstant(double value) {
return getProcess().getBrownianMotion().getRandomVariableForConstant(value);
}
@Override
public int getNumberOfComponents() {
return initialValues.length;
}
@Override
public int getNumberOfAssets() {
return getNumberOfComponents();
}
@Override
public String toString() {
return "MonteCarloMultiAssetBlackScholesModel [initialValues="
+ Arrays.toString(initialValues) + ", riskFreeRate="
+ riskFreeRate + ", volatilities="
+ Arrays.toString(volatilities) + ", factorLoadings="
+ Arrays.toString(factorLoadings) + "]";
}
/**
* Returns the risk free rate parameter of this model.
*
* @return Returns the riskFreeRate.
*/
public double getRiskFreeRate() {
return riskFreeRate;
}
/**
* Returns the volatility parameters of this model.
*
* @return Returns the volatilities.
*/
public double[] getVolatilities() {
return volatilities;
}
/**
* @return The number of paths.
* @see net.finmath.montecarlo.process.AbstractProcess#getNumberOfPaths()
*/
@Override
public int getNumberOfPaths() {
return getProcess().getNumberOfPaths();
}
@Override
public MonteCarloMultiAssetBlackScholesModel getCloneWithModifiedData(Map<String, Object> dataModified) throws CalculationException {
double[] newInitialValues = initialValues;
double newRiskFreeRate = riskFreeRate;
double[] newVolatilities = volatilities;
double[][] newCorrelations = null;// = correlations;
if(dataModified.containsKey("initialValues")) newInitialValues = (double[]) dataModified.get("initialValues");
if(dataModified.containsKey("riskFreeRate")) newRiskFreeRate = ((Double)dataModified.get("riskFreeRate")).doubleValue();
if(dataModified.containsKey("volatilities")) newVolatilities = (double[]) dataModified.get("volatilities");
if(dataModified.containsKey("correlations")) newCorrelations = (double[][]) dataModified.get("correlations");
return new MonteCarloMultiAssetBlackScholesModel(getTimeDiscretization(), getNumberOfPaths(), newInitialValues, newRiskFreeRate, newVolatilities, newCorrelations);
}
@Override
public AssetModelMonteCarloSimulationInterface getCloneWithModifiedSeed(int seed) {
// TODO Auto-generated method stub
return null;
}
}