com.opengamma.analytics.financial.model.finitedifference.applications.TwoStateMarkovChainDensity.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.model.finitedifference.applications.TwoStateMarkovChainDensity.java

Source

/**
 * 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.math.function.Function;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.surface.FunctionalDoublesSurface;

/**
 *  Solves a coupled forward PDE (i.e. coupled Fokker-Plank) for the density of an asset when the process is CEV with vol levels determined by a
 *  two state Markov chain. The densities, p(t,s,state1) & p(t,s,state2), are such that int_{0}^{\infty} p(t,s,stateX) ds gives the probability
 *  of being in state X at time t, and (p(t,s,state1)+p(t,s,state2))*ds is the probability that the asset with be between s and s + ds at time t.   
 */
public class TwoStateMarkovChainDensity {
    private static final double THETA = 1.0;

    private final ConvectionDiffusionPDE1DCoupledCoefficients _data1;
    private final ConvectionDiffusionPDE1DCoupledCoefficients _data2;
    private final Function1D<Double, Double> _initCon11;
    private final Function1D<Double, Double> _initCon12;

    public TwoStateMarkovChainDensity(final ForwardCurve forward, final double vol1, final double deltaVol,
            final double lambda12, final double lambda21, final double probS1, final double beta1,
            final double beta2) {
        this(forward,
                new TwoStateMarkovChainDataBundle(vol1, vol1 + deltaVol, lambda12, lambda21, probS1, beta1, beta2));
    }

    public TwoStateMarkovChainDensity(final ForwardCurve forward, final TwoStateMarkovChainDataBundle data) {
        Validate.notNull(forward, "null forward");
        Validate.notNull(data, "null data");

        _data1 = getCoupledPDEDataBundle(forward, data.getVol1(), data.getLambda12(), data.getLambda21(),
                data.getBeta1());
        _data2 = getCoupledPDEDataBundle(forward, data.getVol2(), data.getLambda21(), data.getLambda12(),
                data.getBeta2());
        _initCon11 = getInitialCondition(forward, data.getP0());
        _initCon12 = getInitialCondition(forward, 1.0 - data.getP0());
    }

    PDEFullResults1D[] solve(final PDEGrid1D grid) {

        //BoundaryCondition lower = new FixedSecondDerivativeBoundaryCondition(0, grid.getSpaceNode(0), true);
        final BoundaryCondition lower = new NeumannBoundaryCondition(0.0, grid.getSpaceNode(0), true);
        //BoundaryCondition lower = new DirichletBoundaryCondition(0.0, grid.getSpaceNode(0));//TODO for beta < 0.5 zero is accessible and thus there will be non-zero 
        //density there
        final BoundaryCondition upper = new DirichletBoundaryCondition(0.0,
                grid.getSpaceNode(grid.getNumSpaceNodes() - 1));

        CoupledPDEDataBundle d1 = new CoupledPDEDataBundle(_data1, _initCon11, lower, upper, grid);
        CoupledPDEDataBundle d2 = new CoupledPDEDataBundle(_data2, _initCon12, lower, upper, grid);

        final CoupledFiniteDifference solver = new CoupledFiniteDifference(THETA, true);
        final PDEResults1D[] res = solver.solve(d1, d2);
        //handle this with generics  
        final PDEFullResults1D res1 = (PDEFullResults1D) res[0];
        final PDEFullResults1D res2 = (PDEFullResults1D) res[1];
        return new PDEFullResults1D[] { res1, res2 };
    }

    private Function1D<Double, Double> getInitialCondition(final ForwardCurve forward, final double initialProb) {
        //using a log-normal distribution with a very small Standard deviation as a proxy for a Dirac delta
        return new Function1D<Double, Double>() {
            private final double _volRootTOffset = 0.01;

            @Override
            public Double evaluate(final Double s) {
                if (s <= 0 || initialProb == 0) {
                    return 0.0;
                }
                final double x = Math.log(s / forward.getSpot());
                final NormalDistribution dist = new NormalDistribution(0, _volRootTOffset);
                return initialProb * dist.getPDF(x) / s;
            }
        };
    }

    private ConvectionDiffusionPDE1DCoupledCoefficients getCoupledPDEDataBundle(final ForwardCurve forward,
            final double vol, final double lambda1, final double lambda2, final double beta) {

        final Function<Double, Double> a = new Function<Double, Double>() {
            @Override
            public Double evaluate(final Double... ts) {
                Validate.isTrue(ts.length == 2);
                double s = ts[1];
                if (s <= 0.0) { //TODO review how to handle absorption 
                    s = -s;
                }
                return -Math.pow(s, 2 * beta) * vol * vol / 2;
            }
        };

        final Function<Double, Double> b = new Function<Double, Double>() {
            @Override
            public Double evaluate(final Double... ts) {
                Validate.isTrue(ts.length == 2);
                final double t = ts[0];
                double s = ts[1];
                if (s < 0.0) {
                    s = -s;
                }
                final double temp = (s < 0.0 ? 0.0 : 2 * vol * vol * beta * Math.pow(s, 2 * (beta - 1)));
                return s * (forward.getDrift(t) - temp);
            }
        };

        final Function<Double, Double> c = new Function<Double, Double>() {
            @Override
            public Double evaluate(final Double... ts) {
                Validate.isTrue(ts.length == 2);
                final double t = ts[0];
                double s = ts[1];

                if (s < 0.) {
                    s = -s;
                }
                double temp = (beta == 1.0 ? 1.0 : Math.pow(s, 2 * (beta - 1)));
                if (s < 0) {
                    temp = 0.0;
                }
                return lambda1 + forward.getDrift(t) - vol * vol * beta * (2 * beta - 1) * temp;
            }
        };

        return new ConvectionDiffusionPDE1DCoupledCoefficients(FunctionalDoublesSurface.from(a),
                FunctionalDoublesSurface.from(b), FunctionalDoublesSurface.from(c), -lambda2);
    }
}