Package com.opengamma.analytics.financial.equity.variance.pricing

Source Code of com.opengamma.analytics.financial.equity.variance.pricing.EquityVarianceSwapBackwardsPurePDE

/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.equity.variance.pricing;

import static com.opengamma.analytics.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils.getLowerBoundIndex;

import com.opengamma.analytics.financial.model.finitedifference.BoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDE1DCoefficients;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDE1DStandardCoefficients;
import com.opengamma.analytics.financial.model.finitedifference.ConvectionDiffusionPDESolver;
import com.opengamma.analytics.financial.model.finitedifference.ExponentialMeshing;
import com.opengamma.analytics.financial.model.finitedifference.HyperbolicMeshing;
import com.opengamma.analytics.financial.model.finitedifference.MeshingFunction;
import com.opengamma.analytics.financial.model.finitedifference.NeumannBoundaryCondition;
import com.opengamma.analytics.financial.model.finitedifference.PDE1DDataBundle;
import com.opengamma.analytics.financial.model.finitedifference.PDEGrid1D;
import com.opengamma.analytics.financial.model.finitedifference.PDEResults1D;
import com.opengamma.analytics.financial.model.finitedifference.ThetaMethodFiniteDifference;
import com.opengamma.analytics.financial.model.finitedifference.applications.PDE1DCoefficientsProvider;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.volatility.local.LocalVolatilitySurfaceMoneyness;
import com.opengamma.analytics.financial.model.volatility.local.PureLocalVolatilitySurface;
import com.opengamma.analytics.math.function.Function1D;

/**
* Class to calculate the expected variance (<b>not</b> annualised) of an equity variance swap when a discount curve, affine dividends and a <b>pure</b> local volatility surface is
* specified. See White (2012), Equity Variance Swap with Dividends, for details of the model.
*/
public class EquityVarianceSwapBackwardsPurePDE {
  /** Bunching parameter for the time mesh */
  private static final double LAMBDA_T = -4.0;
  /** Bunching parameter for the space mesh */
  private static final double LAMBDA_X = 0.1;
  /** Maximum range for volatility */
  private static final double SIGMA = 4.0//TODO changed from 6.0

  /** Default weighting the averaging an explicit and implicit scheme */
  private static final double DEFAULT_THETA = 0.5;
  /** */
  private static final PDE1DCoefficientsProvider PDE_PROVIDER = new PDE1DCoefficientsProvider();

  /** */
  private final int _nTimeSteps;
  /** */
  private final int _nSpaceSteps;
  /** */
  private final ConvectionDiffusionPDESolver _solver;

  /**
   * Sets up the PDE
   */
  public EquityVarianceSwapBackwardsPurePDE() {
    _nTimeSteps = 100;
    _nSpaceSteps = 100;
    _solver = new ThetaMethodFiniteDifference(DEFAULT_THETA, false);
  }

  /**
   * Computes the expected variance by solving the backward PDE.
   * @param spot The current level of the stock or index, greater than zero
   * @param discountCurve The risk free interest rate curve, not null
   * @param dividends The dividends structure, not null
   * @param expiry The expiry of the variance swap, greater than zero
   * @param pureLocalVolSurface A <b>pure</b> local volatility surface, not null
   * @return The expected variance with and without adjustments for the dividend payments (the former is usually the case for single stock and the latter for indices)
   */
  public double[] expectedVariance(final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends, final double expiry,
      final PureLocalVolatilitySurface pureLocalVolSurface) {
    final EquityDividendsCurvesBundle divs = new EquityDividendsCurvesBundle(spot, discountCurve, dividends);
    final double logNoDivFwd = Math.log(spot) + discountCurve.getInterestRate(expiry) * expiry;
    //"convert" to a LocalVolatilitySurfaceMoneyness _ DO NOT interpret this as an actual LocalVolatilitySurfaceMoneyness
    final LocalVolatilitySurfaceMoneyness localVolSurface = new LocalVolatilitySurfaceMoneyness(pureLocalVolSurface.getSurface(), new ForwardCurve(1.0));

    final ConvectionDiffusionPDE1DStandardCoefficients pde = PDE_PROVIDER.getLogBackwardsLocalVol(expiry, localVolSurface);
    final Function1D<Double, Double> logPayoff = getLogPayoff(divs, expiry);

    //evaluate the log-payoff on a nominally six sigma range
    final double atmVol = pureLocalVolSurface.getVolatility(expiry, 1.0);
    final double yMin = -Math.sqrt(expiry) * atmVol * SIGMA;
    final double yMax = -yMin;

    final BoundaryCondition lower = new NeumannBoundaryCondition(getLowerBoundaryCondition(divs, expiry), yMin, true);
    final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, yMax, false);

    final MeshingFunction timeMesh = new ExponentialMeshing(0, expiry, _nTimeSteps, LAMBDA_T);
    final MeshingFunction spaceMesh = new ExponentialMeshing(yMin, yMax, _nSpaceSteps, LAMBDA_X);

    final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh);
    final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde, logPayoff, lower, upper, grid);
    final PDEResults1D res = _solver.solve(db);

    final int index = getLowerBoundIndex(res.getGrid().getSpaceNodes(), 0.0);
    final double x1 = res.getSpaceValue(index);
    final double x2 = res.getSpaceValue(index + 1);
    final double y1 = res.getFunctionValue(index);
    final double y2 = res.getFunctionValue(index + 1);
    final double dx = x2 - x1;
    final double eLogS = (x2 * y1 - x1 * y2) / dx;
    final double rvNoDivs = -2 * (eLogS - logNoDivFwd);

    final int nDivs = dividends.getNumberOfDividends();

    double corrDivAdj = 0;
    double uncorrDivAdj = 0;
    int i = 0;
    while (nDivs > 0 && i < nDivs && dividends.getTau(i) <= expiry) {
      corrDivAdj += getCorrection(dividends, divs, i, true, pureLocalVolSurface);
      uncorrDivAdj += getCorrection(dividends, divs, i, false, pureLocalVolSurface);
      i++;
    }

    //TODO add correction terms
    return new double[] {rvNoDivs + 2 * corrDivAdj, rvNoDivs + 2 * uncorrDivAdj };
  }

  private double getCorrection(final AffineDividends ad, final EquityDividendsCurvesBundle curves, final int index,
      final boolean correctForDividends, final PureLocalVolatilitySurface plv) {
    final double alpha = ad.getAlpha(index);
    if (alpha == 0.0) {
      final double temp = Math.log(1 - ad.getBeta(index));
      return correctForDividends ? temp : temp + 0.5 * temp * temp;
    }
    final double tau = ad.getTau(index);
    final double atmVol = plv.getVolatility(tau, 1.0);
    final double yMin = -Math.sqrt(tau) * atmVol * SIGMA;
    final double yMax = -yMin;

    final LocalVolatilitySurfaceMoneyness localVolSurface = new LocalVolatilitySurfaceMoneyness(plv.getSurface(), new ForwardCurve(1.0));
    final ConvectionDiffusionPDE1DStandardCoefficients pde = PDE_PROVIDER.getLogBackwardsLocalVol(tau, localVolSurface);
    final Function1D<Double, Double> initalCond = getCorrectionInitialCondition(ad, curves, index, correctForDividends);

    final BoundaryCondition lower = new NeumannBoundaryCondition(getCorrectionLowerBoundaryCondition(ad, curves, index, correctForDividends, index), yMin, true);
    final BoundaryCondition upper = new NeumannBoundaryCondition(0.0, yMax, false);

    final MeshingFunction timeMesh = new ExponentialMeshing(0, tau, _nTimeSteps, LAMBDA_T);
    final MeshingFunction spaceMesh = new HyperbolicMeshing(yMin, yMax, 0.0, _nSpaceSteps, LAMBDA_X);

    final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh);
    final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde, initalCond, lower, upper, grid);
    final PDEResults1D res = _solver.solve(db);

    final int gIndex = getLowerBoundIndex(res.getGrid().getSpaceNodes(), 0.0);
    final double x1 = res.getSpaceValue(gIndex);
    final double x2 = res.getSpaceValue(gIndex + 1);
    final double y1 = res.getFunctionValue(gIndex);
    final double y2 = res.getFunctionValue(gIndex + 1);
    final double dx = x2 - x1;
    return (x2 * y1 - x1 * y2) / dx;
  }

  private Function1D<Double, Double> getLogPayoff(final EquityDividendsCurvesBundle divs, final double expiry) {
    final double f = divs.getF(expiry);
    final double d = divs.getD(expiry);
    final double logF = (d == 0 ? Math.log(f) : 0.0);
    return new Function1D<Double, Double>() {

      @Override
      public Double evaluate(final Double y) {
        if (d == 0) {
          return logF + y;
        }
        final double x = Math.exp(y);
        final double s = (f - d) * x + d;
        return Math.log(s);
      }
    };
  }

  //TODO this needs to be checked
  private Function1D<Double, Double> getLowerBoundaryCondition(final EquityDividendsCurvesBundle divs, final double expiry) {
    final double f = divs.getF(expiry);
    final double d = divs.getD(expiry);
    return new Function1D<Double, Double>() {

      @Override
      public Double evaluate(final Double y) {
        if (d == 0) {
          return 1.0;
        }
        final double x = Math.exp(y);
        return (f - d) * x / ((f - d) * x + d);
      }
    };
  }

  private Function1D<Double, Double> getCorrectionInitialCondition(final AffineDividends ad, final EquityDividendsCurvesBundle curves, final int index,
      final boolean correctForDividends) {
    final double tau = ad.getTau(index);
    final double alpha = ad.getAlpha(index);
    final double beta = ad.getBeta(index);
    final double f = curves.getF(tau);
    final double d = curves.getD(tau);

    return new Function1D<Double, Double>() {

      @Override
      public Double evaluate(final Double y) {

        final double x = Math.exp(y);
        final double s = (f - d) * x + d;
        final double temp = Math.log(s * (1 - beta) / (s + alpha));
        return correctForDividends ? temp : temp + 0.5 * temp * temp;
      }
    };
  }

  private Function1D<Double, Double> getCorrectionLowerBoundaryCondition(final AffineDividends ad, final EquityDividendsCurvesBundle curves, final int index,
      final boolean correctForDividends, final double yMin) {
    final double tau = ad.getTau(index);
    final double alpha = ad.getAlpha(index);
    final double beta = ad.getBeta(index);
    final double f = curves.getF(tau);
    final double d = curves.getD(tau);
    final double x = Math.exp(yMin);
    //the assumption is that for very small x, x is stuck at low values, so the stock value at the dividend is just this projected by the forward
    final double s = (f - d) * x + d;
    final double temp = Math.log(s * (1 - beta) / (s + alpha));
    final double res = (correctForDividends ? 1.0 : 1.0 + temp) * alpha / s / (s + alpha) * (f - d);
    //not time dependent
    return new Function1D<Double, Double>() {

      @Override
      public Double evaluate(final Double t) {
        return res;
      }
    };
  }

}
TOP

Related Classes of com.opengamma.analytics.financial.equity.variance.pricing.EquityVarianceSwapBackwardsPurePDE

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.