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

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

/**
* 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 com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceConverter;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceMoneyness;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceStrike;
import com.opengamma.analytics.financial.model.volatility.surface.PureImpliedVolatilitySurface;
import com.opengamma.analytics.math.FunctionUtils;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.integration.Integrator1D;
import com.opengamma.analytics.math.integration.RungeKuttaIntegrator1D;

/**
* Class to calculate the expected variance (NOT annualised) of an equity variance swap when a discount curve, affine dividends and either a PURE implied volatility surface or
* (classic) implied volatility surface is specified. See White (2012), Equity Variance Swap with Dividends, for details of the model
*/
public class EquityVarianceSwapStaticReplication {
  /** The integrator */
  private static final Integrator1D<Double, Double> INTEGRATOR = new RungeKuttaIntegrator1D();

  /**
   * Computes the computes the expected variance with and without adjustments for the dividend payments, by computing the price of a log-contract with expiry coinciding with that
   *  of the variance swap, and the value of the dividend corrections at all dividend dates, using the method of static replication from pure option prices obtained from the
   *  <b>pure</b> implied volatility surface (The pure< implied volatility is a number that put into Black formula (with unit forward) gives the price of puts and calls of the pure
   *  stock).
   * @param spot The current level of the stock or index
   * @param discountCurve The risk free interest rate curve
   * @param dividends The dividends structure
   * @param expiry The expiry of the variance swap
   * @param volSurface A <b>pure</b> implied volatility surface -
   * @return The expected variance (<b>not</b> annualised) 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 PureImpliedVolatilitySurface volSurface) {

    final EquityDividendsCurvesBundle divCurves = new EquityDividendsCurvesBundle(spot, discountCurve, dividends);

    final double terminalFwd = divCurves.getF(expiry);
    final double logNoDivFwd = Math.log(spot) + discountCurve.getInterestRate(expiry) * expiry;
    final double logContract = integrate(getLogPayoffIntegrand(expiry, volSurface, divCurves)) + Math.log(terminalFwd);

    double corrDivAdj = 0;
    double uncorrDivAdj = 0;
    int index = 0;
    final int n = dividends.getNumberOfDividends();
    while (n > 0 && index < n && dividends.getTau(index) <= expiry) {
      final double f = divCurves.getF(dividends.getTau(index));
      corrDivAdj += integrate(getCorrectedDividendAdjustmentIntegrand(index, volSurface, divCurves, dividends)) + getCorrectedDividendAdjustment(f, index, dividends);
      uncorrDivAdj += integrate(getUncorrectedDividendAdjustmentIntegrand(index, volSurface, divCurves, dividends)) + getUncorrectedDividendAdjustment(f, index, dividends);
      index++;
    }

    final double rvNoDivs = -2 * (logContract - logNoDivFwd);
    final double rvCorrDivs = rvNoDivs + 2 * corrDivAdj;
    final double rvUncorrDivs = rvNoDivs + 2 * uncorrDivAdj;
    return new double[] {rvCorrDivs, rvUncorrDivs };
  }

  /**
   * Computes the computes the expected variance with and without adjustments for the dividend payments, by computing the price of a log-contract with expiry coinciding with that
   *  of the variance swap, and the value of the dividend corrections at all dividend dates, using the method of static replication from option prices obtained from the
   * implied volatility surface<p> <b>NOTE</b> For finite cash dividends, there is a arbitrage if the implied volatility remains smooth across the dividend date, hence simple interpolation
   * from market option prices to form a (smooth) implied volatility surface WILL introduce arbitrage. It is therefore better to work with a pure implied volatility surface.
   * @param spot The current level of the stock or index
   * @param discountCurve The risk free interest rate curve
   * @param dividends The dividends structure
   * @param expiry The expiry of the variance swap
   * @param volSurfaceStrike A implied volatility surface
   * @return The expected variance (<b>not</b> annualised) 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 BlackVolatilitySurfaceStrike volSurfaceStrike) {

    final EquityDividendsCurvesBundle divCurves = new EquityDividendsCurvesBundle(spot, discountCurve, dividends);
    final BlackVolatilitySurfaceMoneyness volSurface = BlackVolatilitySurfaceConverter.toMoneynessSurface(volSurfaceStrike, new ForwardCurve(divCurves.getF()));

    final double terminalFwd = divCurves.getF(expiry);
    final double logNoDivFwd = Math.log(spot) + discountCurve.getInterestRate(expiry) * expiry;
    final double logContract = integrate(getLogPayoffIntegrand(expiry, volSurface)) + Math.log(terminalFwd);

    double corrDivAdj = 0;
    double uncorrDivAdj = 0;
    int index = 0;
    final int n = dividends.getNumberOfDividends();
    while (n > 0 && index < n && dividends.getTau(index) <= expiry) {
      final double f = divCurves.getF(dividends.getTau(index));
      corrDivAdj += integrate(getCorrectedDividendAdjustmentIntegrand(index, volSurface, dividends)) + getCorrectedDividendAdjustment(f, index, dividends);
      uncorrDivAdj += integrate(getUncorrectedDividendAdjustmentIntegrand(index, volSurface, dividends)) + getUncorrectedDividendAdjustment(f, index, dividends);
      index++;
    }

    final double rvNoDivs = -2 * (logContract - logNoDivFwd);
    final double rvCorrDivs = rvNoDivs + 2 * corrDivAdj;
    final double rvUncorrDivs = rvNoDivs + 2 * uncorrDivAdj;
    return new double[] {rvCorrDivs, rvUncorrDivs };
  }

  private double integrate(final Function1D<Double, Double> func) {
    final double put = INTEGRATOR.integrate(func, 0.0, 1.0);
    final double call = INTEGRATOR.integrate(func, 1.0, 50.0); //TODO set upper limit from tolerance
    return put + call;
  }

  /**
   * The (non-discounted) value of the log-payoff, $\mathbb{E}[S_T]$, can be computed as the log of the forward, $\log F_T$ plus the integral of this function from
   * 0 to infinity (because of the non-smoothness when switching from puts at calls at x = 1, it is better to split the integral in two, 0 to 1 & 1 to infinity).
   * @param expiry log-payoff expiry
   * @param volSurface pure implied volatility surface
   * @param divCurves dividend curves
   * @return A function that integrates the log payoff
   */
  private Function1D<Double, Double> getLogPayoffIntegrand(final double expiry, final PureImpliedVolatilitySurface volSurface, final EquityDividendsCurvesBundle divCurves) {
    final double f = divCurves.getF(expiry);
    final double d = divCurves.getD(expiry);

    final Function1D<Double, Double> integrand = new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double x) {
        if (x == 0.0) {
          return 0.0;
        }
        final boolean isCall = x >= 1.0;
        final double vol = volSurface.getVolatility(expiry, x);
        final double price = BlackFormulaRepository.price(1.0, x, expiry, vol, isCall);
        final double weight = -FunctionUtils.square((f - d) / ((f - d) * x + d));
        return price * weight;
      }
    };
    return integrand;
  }

  private Function1D<Double, Double> getLogPayoffIntegrand(final double expiry, final BlackVolatilitySurfaceMoneyness volSurface) {

    final Function1D<Double, Double> integrand = new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double x) {
        if (x == 0) {
          return 0.0;
        }
        final boolean isCall = x >= 1.0;
        final double vol = volSurface.getVolatilityForMoneyness(expiry, x);
        final double otmPrice = BlackFormulaRepository.price(1.0, x, expiry, vol, isCall);
        final double weight = -1 / x / x;
        return otmPrice * weight;
      }
    };
    return integrand;
  }

  private Function1D<Double, Double> getCorrectedDividendAdjustmentIntegrand(final int dividendIndex, final PureImpliedVolatilitySurface volSurface,
      final EquityDividendsCurvesBundle divCurves, final AffineDividends dividends) {
    final double tau = dividends.getTau(dividendIndex);
    final double f = divCurves.getF(tau);
    final double d = divCurves.getD(tau);
    final double alpha = dividends.getAlpha(dividendIndex);
    final double fMd2 = FunctionUtils.square(f - d);

    final Function1D<Double, Double> integrand = new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double x) {
        if (x == 0) {
          return 0.0;
        }
        final boolean isCall = x >= 1.0;
        final double vol = volSurface.getVolatility(tau, x);
        final double otmPurePrice = BlackFormulaRepository.price(1.0, x, tau, vol, isCall);
        final double s = (f - d) * x + d;
        final double weight = -fMd2 * alpha * (2 * s + alpha) / s / s / FunctionUtils.square(s + alpha);
        return otmPurePrice * weight;
      }
    };
    return integrand;
  }

  private Function1D<Double, Double> getCorrectedDividendAdjustmentIntegrand(final int dividendIndex, final BlackVolatilitySurfaceMoneyness volSurface,
      final AffineDividends dividends) {
    final double tau = dividends.getTau(dividendIndex);
    final double alpha = dividends.getAlpha(dividendIndex);
    final double f = volSurface.getForwardCurve().getForward(tau);

    final Function1D<Double, Double> integrand = new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double x) {
        if (x == 0) {
          return 0.0;
        }
        final boolean isCall = x >= 1.0;
        final double vol = volSurface.getVolatilityForMoneyness(tau, x);
        final double otmPrice = BlackFormulaRepository.price(1.0, x, tau, vol, isCall);

        final double weight = -alpha * (2 * x * f + alpha) / x / x / FunctionUtils.square(x * f + alpha);
        return otmPrice * weight;
      }
    };
    return integrand;
  }

  private Function1D<Double, Double> getUncorrectedDividendAdjustmentIntegrand(final int dividendIndex, final PureImpliedVolatilitySurface volSurface,
      final EquityDividendsCurvesBundle divCurves, final AffineDividends dividends) {
    final double tau = dividends.getTau(dividendIndex);
    final double f = divCurves.getF(tau);
    final double d = divCurves.getD(tau);
    final double alpha = dividends.getAlpha(dividendIndex);
    final double beta = dividends.getBeta(dividendIndex);
    final double fMd2 = FunctionUtils.square(f - d);

    final Function1D<Double, Double> integrand = new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double x) {
        if (x == 0) {
          return 0.0;
        }
        final boolean isCall = x >= 1.0;
        final double vol = volSurface.getVolatility(tau, x);
        final double otmPurePrice = BlackFormulaRepository.price(1.0, x, tau, vol, isCall);
        final double s = (f - d) * x + d;
        final double sPalpha = s + alpha;
        final double h = Math.log(s * (1 - beta) / sPalpha);
        final double dH = alpha / s / sPalpha;
        final double ddH = -alpha * (s + sPalpha) / s / s / sPalpha / sPalpha;
        final double weight = fMd2 * ((1 + h) * ddH + dH * dH);
        return otmPurePrice * weight;
      }
    };
    return integrand;
  }

  private Function1D<Double, Double> getUncorrectedDividendAdjustmentIntegrand(final int dividendIndex, final BlackVolatilitySurfaceMoneyness volSurface,
      final AffineDividends dividends) {
    final double tau = dividends.getTau(dividendIndex);
    final double alpha = dividends.getAlpha(dividendIndex);
    final double beta = dividends.getBeta(dividendIndex);
    final double f = volSurface.getForwardCurve().getForward(tau);

    final Function1D<Double, Double> integrand = new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double x) {
        if (x == 0) {
          return 0.0;
        }
        final boolean isCall = x >= 1.0;
        final double vol = volSurface.getVolatilityForMoneyness(tau, x);
        final double otmPrice = BlackFormulaRepository.price(1.0, x, tau, vol, isCall);
        final double s = x * f;
        final double sPalpha = s + alpha;
        final double h = Math.log(s * (1 - beta) / sPalpha);
        final double dH = alpha / x / sPalpha;
        final double ddH = -alpha * (s + sPalpha) / x / x / sPalpha / sPalpha;
        final double weight = ((1 + h) * ddH + dH * dH);
        return otmPrice * weight;
      }
    };
    return integrand;
  }

  private double getCorrectedDividendAdjustment(final double s, final int dividendIndex, final AffineDividends dividends) {
    final double alpha = dividends.getAlpha(dividendIndex);
    final double beta = dividends.getBeta(dividendIndex);
    return Math.log(s * (1 - beta) / (s + alpha));
  }

  private double getUncorrectedDividendAdjustment(final double s, final int dividendIndex, final AffineDividends dividends) {
    final double alpha = dividends.getAlpha(dividendIndex);
    final double beta = dividends.getBeta(dividendIndex);
    final double h = Math.log(s * (1 - beta) / (s + alpha));
    return h + 0.5 * h * h;
  }

}
TOP

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

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.