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

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

/**
* 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.equity.variance.EquityVarianceSwap;
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.smile.fitting.interpolation.GeneralSmileInterpolator;
import com.opengamma.analytics.financial.model.volatility.smile.fitting.interpolation.SmileInterpolatorSpline;
import com.opengamma.analytics.financial.model.volatility.smile.fitting.sabr.SmileSurfaceDataBundle;
import com.opengamma.analytics.financial.model.volatility.smile.fitting.sabr.StandardSmileSurfaceDataBundle;
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.financial.model.volatility.surface.VolatilitySurfaceInterpolator;
import com.opengamma.analytics.math.function.Function;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.interpolation.Interpolator1DFactory;
import com.opengamma.analytics.math.surface.FunctionalDoublesSurface;
import com.opengamma.analytics.math.surface.Surface;
import com.opengamma.util.ArgumentChecker;

/**
*
*/
public final class EquityVarianceSwapStaticReplicationPricer {
  /** Prices using static replication */
  private static final EquityVarianceSwapStaticReplication VAR_SWAP_CALCULATOR = new EquityVarianceSwapStaticReplication();

  /** The smile interpolator to use */
  private final VolatilitySurfaceInterpolator _surfaceInterpolator;

  /**
   * Builder class for this pricer.
   * <p>
   * The following default values are supplied:
   * <ul>
   * <li> Smile interpolator = spline
   * <li> Time interpolator = natural cubic spline with linear extrapolation
   * <li> Use log time = true
   * <li> Use integrated variance = true
   * <li> Use log value = true
   * </ul>
   */
  public static final class Builder {
    /** The smile interpolator to use */
    private final GeneralSmileInterpolator _smileInterpolator;
    /** The time interpolator to use */
    private final Interpolator1D _timeInterpolator;
    /** Use log time */
    private final boolean _useLogTime;
    /** Use integrated variance */
    private final boolean _useIntegratedVariance;
    /** Use log value */
    private final boolean _useLogValue;

    /* package */Builder() {
      this(new SmileInterpolatorSpline(), CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.NATURAL_CUBIC_SPLINE, Interpolator1DFactory.LINEAR_EXTRAPOLATOR),
          true, true, true);
    }

    /* package */Builder(final GeneralSmileInterpolator smileInterpolator, final Interpolator1D timeInterpolator, final boolean useLogTime, final boolean useIntegratedVariance,
        final boolean useLogValue) {
      ArgumentChecker.notNull(smileInterpolator, "smile interpolator");
      ArgumentChecker.notNull(timeInterpolator, "time interpolator");
      _smileInterpolator = smileInterpolator;
      _timeInterpolator = timeInterpolator;
      _useLogTime = useLogTime;
      _useIntegratedVariance = useIntegratedVariance;
      _useLogValue = useLogValue;
    }

    /**
     * @param smileInterpolator The smile interpolator, not null
     * @return a new Builder with this smile interpolator
     */
    public Builder withSmileInterpolator(final GeneralSmileInterpolator smileInterpolator) {
      return new Builder(smileInterpolator, _timeInterpolator, _useLogTime, _useIntegratedVariance, _useLogValue);
    }

    /**
     * @param timeInterpolator The time interpolator, not null
     * @return a new Builder with this time interpolator
     */
    public Builder timeInterpolator(final Interpolator1D timeInterpolator) {
      return new Builder(_smileInterpolator, timeInterpolator, _useLogTime, _useIntegratedVariance, _useLogValue);
    }

    /**
     * @param useLogTime true if log time is to be used
     * @return a new Builder with the log time parameter set to true
     */
    public Builder useLogTime(final boolean useLogTime) {
      return new Builder(_smileInterpolator, _timeInterpolator, useLogTime, _useIntegratedVariance, _useLogValue);
    }

    /**
     * @param useIntegratedVariance true if integrated variance is to be used
     * @return a new Builder with the integrated variance parameter set to true
     */
    public Builder useIntegratedVariance(final boolean useIntegratedVariance) {
      return new Builder(_smileInterpolator, _timeInterpolator, _useLogTime, useIntegratedVariance, _useLogValue);
    }

    /**
     * @param useLogValue true if log values are to be used
     * @return a new Builder with the log value parameter set to true
     */
    public Builder useLogValue(final boolean useLogValue) {
      return new Builder(_smileInterpolator, _timeInterpolator, _useLogTime, _useIntegratedVariance, useLogValue);
    }

    /* package */GeneralSmileInterpolator getSmileInterpolator() {
      return _smileInterpolator;
    }

    /* package */Interpolator1D getTimeInterpolator() {
      return _timeInterpolator;
    }

    /* package */boolean useLogTime() {
      return _useLogTime;
    }

    /* package */boolean useIntegratedVariance() {
      return _useIntegratedVariance;
    }

    /* package */boolean useLogValue() {
      return _useLogValue;
    }

    /**
     * @return The pricer instance
     */
    @SuppressWarnings("synthetic-access")
    public EquityVarianceSwapStaticReplicationPricer create() {
      return new EquityVarianceSwapStaticReplicationPricer(this);
    }
  }

  /**
   * Provides a builder that can construct a pricer with values other than the defaults
   * @return The builder
   */
  public static Builder builder() {
    return new Builder();
  }

  private EquityVarianceSwapStaticReplicationPricer(final Builder builder) {
    _surfaceInterpolator = new VolatilitySurfaceInterpolator(builder.getSmileInterpolator(), builder.getTimeInterpolator(), builder.useLogTime(),
        builder.useIntegratedVariance(), builder.useLogValue());
  }

  /**
   * Calculates the price of an equity variance swap from OTM option prices. The surface used is a pure implied volatility surface.
   * @param swap The details of the equity variance swap, not null
   * @param spot Current level of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param expiries The strips of option expiries, not null
   * @param strikes The strikes for each option strip, not null. Must have the same number of strips as expiries.
   * @param otmPrices The <b>out-of-the-money</b> option prices, not null. Must have the same number of strips as expiries and values as strikes.
   * @return The <b>annualised</b> variance
   */
  public double priceFromOTMPrices(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final double[] expiries, final double[][] strikes, final double[][] otmPrices) {
    ArgumentChecker.notNull(swap, "swap");
    final PureImpliedVolatilitySurface pureSurf = EquityVolatilityToPureVolatilitySurfaceConverter.getConvertedSurface(spot, discountCurve, dividends, expiries, strikes,
        otmPrices, _surfaceInterpolator);

    final double t = swap.getTimeToSettlement();
    //price the variance swap by static replication of the log-payoff and dividend correction terms
    final double[] ev = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, pureSurf);
    //TODO while calculating both with and without div correction is go for testing, don't want it for production
    final double res = (swap.correctForDividends() ? ev[0] : ev[1]) / t;
    return res;
  }

  /**
   * Calculates the delta of a variance swap.
   * <p>
   * The market implied volatilities are treated as invariant to the spot (sticky-strike), and price the variance swap twice with the spot bumped up and down. The
   * pricing itself involves finding pure implied volatilities, then interpolated implied volatility surface and finally the expected variance via static replication.
   * @param swap The details of the equality variance swap, not null
   * @param spot current level of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols the market implied volatilities, not null
   * @return The delta of the variance swap under a sticky-strike assumption <b>scaled by spot</b>
   */
  public double deltaWithStickyStrike(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market volatilities");
    //here we assume the market implied volatilities are invariant to a change of spot
    final double eps = 1e-5;
    final double up = priceFromImpliedVols(swap, (1 + eps) * spot, discountCurve, dividends, marketVols);
    final double down = priceFromImpliedVols(swap, (1 - eps) * spot, discountCurve, dividends, marketVols);
    final double ssDelta = (up - down) / 2 / eps;
    return ssDelta;
  }

  /**
   * Compute the "bucketed vega" of a equity variance swap - the sensitivity of the square-root of the expected variance (since this has the same scale as the implied volatilities)
   * to the market implied volatilities. This is done by bumping each market implied volatility in turn, and computing the sensitivity by finite difference
   * @param swap The details of the equality variance swap
   * @param spot current level of the underlying
   * @param discountCurve The discount curve
   * @param dividends The assumed dividends
   * @param marketVols the market implied volatilities
   * @return bucked vega
   */
  public double[][] bucketedVega(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    final double eps = 1e-5;
    final int nExp = marketVols.getNumExpiries();
    final double[][] res = new double[nExp][];
    for (int i = 0; i < nExp; i++) {
      final int nStrikes = marketVols.getStrikes()[i].length;
      res[i] = new double[nStrikes];
      for (int j = 0; j < nStrikes; j++) {
        final SmileSurfaceDataBundle upVols = marketVols.withBumpedPoint(i, j, eps);
        final double pUp = Math.sqrt(priceFromImpliedVols(swap, spot, discountCurve, dividends, upVols));
        final SmileSurfaceDataBundle downVols = marketVols.withBumpedPoint(i, j, -eps);
        final double pDown = Math.sqrt(priceFromImpliedVols(swap, spot, discountCurve, dividends, downVols));
        res[i][j] = (pUp - pDown) / 2 / eps;
      }
    }
    return res;
  }

  /**
   * Calculates the gamma of a variance swap.
   * <p>
   * The market implied volatilities are treated as invariant to the spot (sticky-strike), and price the variance swap twice with the spot bumped up and down. The
   * pricing itself involves finding pure implied volatilities, then an interpolated implied volatility surface and finally the expected variance via static replication.
   * @param swap The details of the equality variance swap, not null
   * @param spot current level of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols the market implied volatilities, not null
   * @return The gamma of the variance swap under a sticky-strike assumption <b>scaled by spot^2</b>
   */
  public double gammaWithStickyStrike(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market volatilities");
    //here we assume the market implied volatilities are invariant to a change of spot
    final double eps = 1e-5;
    final double up = priceFromImpliedVols(swap, (1 + eps) * spot, discountCurve, dividends, marketVols);
    final double mid = priceFromImpliedVols(swap, spot, discountCurve, dividends, marketVols);
    final double down = priceFromImpliedVols(swap, (1 - eps) * spot, discountCurve, dividends, marketVols);
    final double gamma = (up + down - 2 * mid) / eps / eps;
    return gamma;
  }

  /**
   * Calculates the price of an equity variance swap from implied volatilities. The surface used is a pure implied volatility surface.
   * @param swap The details of the equity variance swap, not null
   * @param spot current level of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols the market implied volatilities, not null
   * @return The <b>annualised</b> variance
   */
  public double priceFromImpliedVols(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    final PureImpliedVolatilitySurface pureSurf = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);
    final double t = swap.getTimeToSettlement();
    //price the variance swap by static replication of the log-payoff and dividend correction terms
    final double[] ev = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, pureSurf);
    //TODO while calculating both with and without div correction is go for testing, don't want it for production
    final double res = (swap.correctForDividends() ? ev[0] : ev[1]) / t;
    return res;
  }

  /**
   * Calculates the delta of a variance swap using a pure implied volatility surface.
   * <p>
   * The (pure) implied volatilities are treated as invariant to the spot (sticky-pure strike which is similar to sticky delta),
   * The variance swap is priced twice with the spot bumped up and down.
   * @param swap The details of the equality variance swap, not null
   * @param spot current level of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols the market implied volatilities, not null
   * @return The delta of the variance swap under a sticky-strike assumption <b>scaled by spot</b>
   */
  public double deltaWithStickyPureStrike(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market volatilities");

    final PureImpliedVolatilitySurface pureSurf = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);

    final double eps = 1e-5;
    final int index = swap.correctForDividends() ? 0 : 1;
    final double t = swap.getTimeToSettlement();

    final double up = VAR_SWAP_CALCULATOR.expectedVariance(spot * (1 + eps), discountCurve, dividends, t, pureSurf)[index];
    final double down = VAR_SWAP_CALCULATOR.expectedVariance(spot * (1 - eps), discountCurve, dividends, t, pureSurf)[index];

    final double ssDelta = (up - down) / 2 / eps / t;
    return ssDelta;
  }

  /**
   * Calculates the gamma of a variance swap using a pure implied volatility surface.
   * <p>
   * The (pure) implied volatilities as invariant to the spot (sticky-pure strike which is similar to sticky delta).
   * The variance swap is priced three times; spot bumped up, down and left unchanged.
   * @param swap The details of the equality variance swap
   * @param spot current level of the underlying
   * @param discountCurve The discount curve
   * @param dividends The assumed dividends
   * @param marketVols the market implied volatilities
   * @return The delta of the variance swap under a sticky-strike assumption <b>scaled by spot</b>
   */
  public double gammaWithStickyPureStrike(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market volatilities");

    final PureImpliedVolatilitySurface pureSurf = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);

    final double eps = 1e-5;
    final int index = swap.correctForDividends() ? 0 : 1;
    final double t = swap.getTimeToSettlement();

    final double up = VAR_SWAP_CALCULATOR.expectedVariance(spot * (1 + eps), discountCurve, dividends, t, pureSurf)[index];
    final double mid = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, pureSurf)[index];
    final double down = VAR_SWAP_CALCULATOR.expectedVariance(spot * (1 - eps), discountCurve, dividends, t, pureSurf)[index];

    final double gamma = (up + down - 2 * mid) / eps / eps / t;
    return gamma;
  }

  /**
   * Calculates the vega of a variance swap to a pure implied volatility surface.
   * <p>
   * The vega is taken as the sensitivity to the <b>square-root</b> of the annualised expected variance (EV) (n.b. this is not the same as the expected volatility)
   * to a parallel shift of the implied volatility surface.
   * @param swap The details of the equality variance swap, not null
   * @param spot current level of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols the market implied volatilities, not null
   * @return The vega
   */
  public double vegaImpVol(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market volatilities");

    final PureImpliedVolatilitySurface piv = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);
    final EquityDividendsCurvesBundle divCurves = new EquityDividendsCurvesBundle(spot, discountCurve, dividends);
    final BlackVolatilitySurfaceStrike iv = VolatilitySurfaceConverter.convertImpliedVolSurface(piv, divCurves);

    final double eps = 1e-5;
    final int index = swap.correctForDividends() ? 0 : 1;
    final double t = swap.getTimeToSettlement();

    //up
    final BlackVolatilitySurfaceStrike ivUp = new BlackVolatilitySurfaceStrike(flooredShiftSurface(iv.getSurface(), eps));
    final double up = Math.sqrt(VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, ivUp)[index] / t);
    //down
    final BlackVolatilitySurfaceStrike ivDown = new BlackVolatilitySurfaceStrike(flooredShiftSurface(iv.getSurface(), -eps));
    final double down = Math.sqrt(VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, ivDown)[index] / t);
    final double vega = (up - down) / 2 / eps;
    return vega;
  }

  /**
   * Calculates the vega of a variance swap to a pure implied volatility surface.
   * <p>
   * The vega is taken as the sensitivity of the <b>square-root</b> of the annualised expected variance (EV) (n.b. this is not the same as the expected volatility)
   * to a parallel shift of the <b>pure</b> implied volatility surface.
   * @param swap The details of the equality variance swap, not null
   * @param spot current level of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols the market implied volatilities, not null
   * @return The vega
   */
  public double vegaPureImpVol(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market volatilities");

    final PureImpliedVolatilitySurface piv = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);

    final double eps = 1e-5;
    final int index = swap.correctForDividends() ? 0 : 1;
    final double t = swap.getTimeToSettlement();

    //up
    final PureImpliedVolatilitySurface ivUp = new PureImpliedVolatilitySurface(flooredShiftSurface(piv.getSurface(), eps));
    final double up = Math.sqrt(VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, ivUp)[index] / t);
    //down
    final PureImpliedVolatilitySurface ivDown = new PureImpliedVolatilitySurface(flooredShiftSurface(piv.getSurface(), -eps));
    final double down = Math.sqrt(VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, ivDown)[index] / t);
    final double vega = (up - down) / 2 / eps;
    return vega;
  }

  /**
   * Compute a delta from the market implied volatilities by first computing a pure implied volatility surface, then treating this as an invariant while the spot is moved.
   * @param swap The variance swap, not null
   * @param spot The spot value of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols The market option prices expressed as implied volatilities, not null
   * @return The delta
   */
  public double deltaFromImpliedVols(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market vols");
    final double eps = 1e-5;

    //this surface is assumed invariant to change in the spot
    final PureImpliedVolatilitySurface pureSurf = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);

    //price the variance swap by static replication of the log-payoff and dividend correction terms
    final double[] evUp = VAR_SWAP_CALCULATOR.expectedVariance((1 + eps) * spot, discountCurve, dividends, swap.getTimeToSettlement(), pureSurf);
    final double[] evDown = VAR_SWAP_CALCULATOR.expectedVariance((1 - eps) * spot, discountCurve, dividends, swap.getTimeToSettlement(), pureSurf);

    final double res = swap.correctForDividends() ? (evUp[0] - evDown[0]) / spot / eps : (evUp[1] - evDown[1]) / spot / eps;
    return res;
  }

  /**
   * Compute sensitivity of an equity variance swap to the dividends. Here the pure volatility surface is assumed to be invariant to a change of dividends
   * @param swap The equity swap, not null
   * @param spot The spot value of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols The market option prices expressed as implied volatilities, not null
   * @return Array of arrays containing dividend sensitivity. For n dividends, there are n rows, each containing two elements: the sensitivity to alpha and beta
   */
  public double[][] dividendSensitivityWithStickyPureVol(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve,
      final AffineDividends dividends, final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market volatilities");
    final double eps = 1e-5;
    final double t = swap.getTimeToObsEnd();
    final int index = swap.correctForDividends() ? 0 : 1;
    final PureImpliedVolatilitySurface pureSurf = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);
    final double base = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, pureSurf)[index];

    final int n = dividends.getNumberOfDividends();

    final double[][] res = new double[n][2];
    for (int i = 0; i < n; i++) {
      //bump alpha
      if (dividends.getAlpha(i) > eps / (1 - eps)) {
        //up
        final AffineDividends daUp = dividends.withAlpha(dividends.getAlpha(i) * (1 + eps) + eps, i);
        final double[] aUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daUp, t, pureSurf);
        //down
        final AffineDividends daDown = dividends.withAlpha(dividends.getAlpha(i) * (1 - eps) - eps, i);
        final double[] aDown = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daDown, t, pureSurf);
        res[i][0] = spot * (aUp[index] - aDown[index]) / 2 / eps / (1 + dividends.getAlpha(i));
      } else {
        //forward difference for zero (or very near zero) alpha
        final AffineDividends daUp = dividends.withAlpha(dividends.getAlpha(i) + eps, i);
        final double[] aUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daUp, t, pureSurf);
        res[i][0] = spot * (aUp[index] - base) / eps;
      }

      //bump beta
      if (dividends.getBeta(i) > eps) {
        final AffineDividends dbUp = dividends.withBeta(dividends.getBeta(i) + eps, i);
        final double[] bUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbUp, swap.getTimeToObsEnd(), pureSurf);
        final AffineDividends dbDown = dividends.withBeta(dividends.getBeta(i) - eps, i);
        final double[] bDown = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbDown, swap.getTimeToObsEnd(), pureSurf);
        res[i][1] = (bUp[index] - bDown[index]) / 2 / eps;
      } else {
        //forward difference for zero (or near zero) beta
        final AffineDividends dbUp = dividends.withBeta(dividends.getBeta(i) + eps, i);
        final double[] bUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbUp, swap.getTimeToObsEnd(), pureSurf);
        res[i][1] = (bUp[index] - base) / eps;
      }
    }

    return res;
  }

  /**
   * Compute sensitivity of an equity variance swap to the dividends. The "market" implied volatility surface is assumed to be invariant to a change of dividends
   * @param swap The equity swap, not null
   * @param spot The spot value of the underlying
   * @param discountCurve The discount curve, not null
   * @param dividends The assumed dividends, not null
   * @param marketVols The market option prices expressed as implied volatilities, not null
   * @return Array of arrays containing dividend sensitivity. For n dividends, there are n rows, each containing two elements: the sensitivity to alpha and beta
   */
  public double[][] dividendSensitivityWithStickyImpliedVol(final EquityVarianceSwap swap, final double spot, final YieldAndDiscountCurve discountCurve,
      final AffineDividends dividends, final SmileSurfaceDataBundle marketVols) {
    ArgumentChecker.notNull(swap, "swap");
    ArgumentChecker.notNull(discountCurve, "discount curve");
    ArgumentChecker.notNull(dividends, "dividends");
    ArgumentChecker.notNull(marketVols, "market vols");
    final double eps = 1e-5;
    final double t = swap.getTimeToObsEnd();
    final int index = swap.correctForDividends() ? 0 : 1;
    final PureImpliedVolatilitySurface pureSurf = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);
    final double base = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, pureSurf)[index];

    final int n = dividends.getNumberOfDividends();

    final double[][] res = new double[n][2];
    for (int i = 0; i < n; i++) {
      //bump alpha
      if (dividends.getAlpha(i) > eps / (1 - eps)) {
        //up
        final AffineDividends daUp = dividends.withAlpha(dividends.getAlpha(i) * (1 + eps) + eps, i);
        final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, daUp, marketVols);
        final double[] aUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daUp, t, pvUp);
        //down
        final AffineDividends daDown = dividends.withAlpha(dividends.getAlpha(i) * (1 - eps) - eps, i);
        final PureImpliedVolatilitySurface pvDown = getPureImpliedVolFromMarket(spot, discountCurve, daDown, marketVols);
        final double[] aDown = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daDown, t, pvDown);
        res[i][0] = spot * (aUp[index] - aDown[index]) / 2 / eps / (1 + dividends.getAlpha(i));
      } else {
        //forward difference for zero (or very near zero) alpha
        final AffineDividends daUp = dividends.withAlpha(dividends.getAlpha(i) + eps, i);
        final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, daUp, marketVols);
        final double[] aUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daUp, t, pvUp);
        res[i][0] = spot * (aUp[index] - base) / eps;
      }

      //bump beta
      if (dividends.getBeta(i) > eps) {
        final AffineDividends dbUp = dividends.withBeta(dividends.getBeta(i) + eps, i);
        final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, dbUp, marketVols);
        final double[] bUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbUp, swap.getTimeToObsEnd(), pvUp);
        final AffineDividends dbDown = dividends.withBeta(dividends.getBeta(i) - eps, i);
        final PureImpliedVolatilitySurface pvDown = getPureImpliedVolFromMarket(spot, discountCurve, dbDown, marketVols);
        final double[] bDown = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbDown, swap.getTimeToObsEnd(), pvDown);
        res[i][1] = (bUp[index] - bDown[index]) / 2 / eps;
      } else {
        //forward difference for zero (or near zero) beta
        final AffineDividends dbUp = dividends.withBeta(dividends.getBeta(i) + eps, i);
        final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, dbUp, marketVols);
        final double[] bUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbUp, swap.getTimeToObsEnd(), pvUp);
        res[i][1] = (bUp[index] - base) / eps;
      }
    }
    return res;
  }

  /**
   * Convert each market implied volatility to an implied volatility of an option on the 'pure' stock, the the VolatilitySurfaceInterpolator to construct a smooth
   * pure implied volatility surface
   * @param spot The spot value of the underlying
   * @param discountCurve The discount curve
   * @param dividends The assumed dividends
   * @param marketVols The market option prices expressed as implied volatilities
   * @return pure implied volatility surface
   */
  private PureImpliedVolatilitySurface getPureImpliedVolFromMarket(final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends,
      final SmileSurfaceDataBundle marketVols) {
    final EquityDividendsCurvesBundle divCurves = new EquityDividendsCurvesBundle(spot, discountCurve, dividends);

    //convert the real option prices to prices of options on pure stock, then find the implied volatility of these options
    final double[][] strikes = marketVols.getStrikes();
    final double[][] vols = marketVols.getVolatilities();
    final int nExp = marketVols.getNumExpiries();
    final double[][] x = new double[nExp][];
    final double[][] pVols = new double[nExp][];
    for (int i = 0; i < nExp; i++) {
      final double t = marketVols.getExpiries()[i];
      final double f = divCurves.getF(t);
      final double d = divCurves.getD(t);
      final int n = strikes[i].length;
      x[i] = new double[n];
      pVols[i] = new double[n];
      for (int j = 0; j < n; j++) {
        final double temp = strikes[i][j] - d;
        ArgumentChecker.isTrue(temp >= 0,
            "strike of {} at expiry {} is less than the discounts value of future cash dividends {}. Either remove this option or change the dividend assumption",
            strikes[i][j], t, d);
        x[i][j] = temp / (f - d);
        pVols[i][j] = volToPureVol(strikes[i][j], f, d, t, vols[i][j]);
      }
    }

    //fit an implied volatility surface to the pure implied vols (as the forward is 1.0, the BlackVolatilitySurfaceMoneyness is numerically identical to the PureImpliedVolatilitySurface
    final SmileSurfaceDataBundle data = new StandardSmileSurfaceDataBundle(new ForwardCurve(1.0), marketVols.getExpiries(), x, pVols);
    final BlackVolatilitySurfaceMoneyness surf = _surfaceInterpolator.getVolatilitySurface(data);
    final PureImpliedVolatilitySurface pureSurf = new PureImpliedVolatilitySurface(surf.getSurface()); //TODO have a direct fitter for PureImpliedVolatilitySurface
    return pureSurf;
  }

  //shift a surface flooring the result at zero
  private static Surface<Double, Double, Double> flooredShiftSurface(final Surface<Double, Double, Double> from, final double amount) {
    final Function<Double, Double> surf = new Function<Double, Double>() {
      @Override
      public Double evaluate(final Double... x) {
        final double temp = from.getZValue(x[0], x[1]) + amount;
        return Math.max(0.0, temp);
      }
    };
    return FunctionalDoublesSurface.from(surf);
  }

  /**
   * Convert the market implied volatility to the implied volatility of an option on the 'pure' stock
   * @param k The Strike
   * @param f The forward
   * @param d The discounted future cash dividends
   * @param t The time-to-expiry
   * @param vol The market implied volatility
   * @return The implied volatility of an option on the 'pure' stock
   */
  private static double volToPureVol(final double k, final double f, final double d, final double t, final double vol) {
    //with no cash dividends both implied volatilities are the same
    if (d == 0) {
      return vol;
    }
    final boolean isCall = k >= f;
    final double p = BlackFormulaRepository.price(f, k, t, vol, isCall);
    final double pp = p / (f - d);
    final double x = (k - d) / (f - d);
    return BlackFormulaRepository.impliedVolatility(pp, 1.0, x, t, vol);
  }
}
TOP

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

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.