/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.finitedifference.applications;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.financial.model.finitedifference.BoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDE1DCoupledCoefficients;
import com.opengamma.analytics.financial.model.finitedifference.CoupledFiniteDifference;
import com.opengamma.analytics.financial.model.finitedifference.CoupledPDEDataBundle;
import com.opengamma.analytics.financial.model.finitedifference.DirichletBoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.NeumannBoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.PDEFullResults1D;
import com.opengamma.analytics.financial.model.finitedifference.PDEGrid1D;
import com.opengamma.analytics.financial.model.finitedifference.PDEResults1D;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.volatility.local.AbsoluteLocalVolatilitySurface;
import com.opengamma.analytics.math.function.Function1D;
/**
* Solves a coupled forward PDE for the price of a call option when the process is CEV with vol levels determined by a two state Markov chain.
*/
public class TwoStateMarkovChainPricer {
private static final CoupledPDEDataBundleProvider BUNDLE_PROVIDER = new CoupledPDEDataBundleProvider();
private final ConvectionDiffusionPDE1DCoupledCoefficients[] _data;
private final ForwardCurve _forward;
private final TwoStateMarkovChainDataBundle _chainDB;
private final Function1D<Double, Double> _initalCond1;
private final Function1D<Double, Double> _initalCond2;
// private final double _lambda12;
// private final double _lambda21;
// private final double _p0;
public TwoStateMarkovChainPricer(final ForwardCurve forward, final TwoStateMarkovChainDataBundle chainDB) {
Validate.notNull(forward, "null forward curve");
Validate.notNull(chainDB, "null MC DB");
_forward = forward;
_chainDB = chainDB;
_data = BUNDLE_PROVIDER.getCoupledForwardPair(forward, chainDB);
_initalCond1 = getInitialCond(forward.getSpot(), chainDB.getP0());
_initalCond2 = getInitialCond(forward.getSpot(), 1.0 - chainDB.getP0());
}
/**
* Solves a coupled forward PDE for the price of a call option when the process is CEV with vol levels determined by a two state Markov chain
* @param forward The forward curve of the underlying asset
* @param chainDB The chain data bundle
* @param localVolOverlay The local volatility overlay
*/
public TwoStateMarkovChainPricer(final ForwardCurve forward, final TwoStateMarkovChainDataBundle chainDB, final AbsoluteLocalVolatilitySurface localVolOverlay) {
Validate.notNull(forward, "null forward curve");
Validate.notNull(chainDB, "null MC DB");
Validate.notNull(localVolOverlay, "null local vol");
_forward = forward;
_chainDB = chainDB;
_data = BUNDLE_PROVIDER.getCoupledForwardPair(forward, chainDB, localVolOverlay);
_initalCond1 = getInitialCond(forward.getSpot(), chainDB.getP0());
_initalCond2 = getInitialCond(forward.getSpot(), 1.0 - chainDB.getP0());
}
PDEFullResults1D solve(final PDEGrid1D grid, final double theta) {
Validate.notNull(grid, "null grid");
Validate.isTrue(0 <= theta && theta <= 1.0, "theta must be in range 0 to 1");
Validate.isTrue(grid.getSpaceNode(0) == 0.0, "space grid must start at zero");
final Function1D<Double, Double> strikeZeroPrice1 = new Function1D<Double, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double t) {
return probState1(t) * _forward.getSpot();
}
};
final Function1D<Double, Double> strikeZeroPrice2 = new Function1D<Double, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double t) {
return (1 - probState1(t)) * _forward.getSpot();
}
};
final BoundaryCondition lower1 = new DirichletBoundaryCondition(strikeZeroPrice1, 0.0);
final BoundaryCondition lower2 = new DirichletBoundaryCondition(strikeZeroPrice2, 0.0);
final double kMax = grid.getSpaceNode(grid.getNumSpaceNodes() - 1);
final BoundaryCondition upper = new NeumannBoundaryCondition(0, kMax, false);
final CoupledPDEDataBundle d1 = new CoupledPDEDataBundle(_data[0], _initalCond1, lower1, upper, grid);
final CoupledPDEDataBundle d2 = new CoupledPDEDataBundle(_data[1], _initalCond2, lower2, upper, grid);
final CoupledFiniteDifference solver = new CoupledFiniteDifference(theta, true);
final PDEResults1D[] res = solver.solve(d1, d2);
final PDEFullResults1D res1 = (PDEFullResults1D) res[0];
final PDEFullResults1D res2 = (PDEFullResults1D) res[1];
final double[][] prices = new double[grid.getNumTimeNodes()][grid.getNumSpaceNodes()];
for (int i = 0; i < grid.getNumTimeNodes(); i++) {
for (int j = 0; j < grid.getNumSpaceNodes(); j++) {
prices[i][j] = res1.getFunctionValue(j, i) + res2.getFunctionValue(j, i);
}
}
return new PDEFullResults1D(grid, prices);
}
private double probState1(final double t) {
final double sum = _chainDB.getLambda12() + _chainDB.getLambda21();
return _chainDB.getSteadyStateProb() + (_chainDB.getP0() - _chainDB.getSteadyStateProb()) * Math.exp(-sum * t);
}
private Function1D<Double, Double> getInitialCond(final double s0, final double p0) {
return new Function1D<Double, Double>() {
@Override
public Double evaluate(Double k) {
return p0 * Math.max(0.0, s0 - k);
}
};
}
}