Package com.opengamma.analytics.financial.model.volatility.smile.function

Source Code of com.opengamma.analytics.financial.model.volatility.smile.function.HestonVolatilityFunction

/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility.smile.function;

import static com.opengamma.analytics.math.interpolation.Interpolator1DFactory.DOUBLE_QUADRATIC;
import static com.opengamma.analytics.math.interpolation.Interpolator1DFactory.FLAT_EXTRAPOLATOR;

import java.util.Arrays;

import org.apache.commons.lang.Validate;

import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.option.pricing.fourier.FFTModelGreeks;
import com.opengamma.analytics.financial.model.option.pricing.fourier.FFTPricer;
import com.opengamma.analytics.financial.model.option.pricing.fourier.HestonCharacteristicExponent;
import com.opengamma.analytics.financial.model.option.pricing.fourier.MartingaleCharacteristicExponent;
import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle;
import com.opengamma.lang.annotation.ExternalFunction;

/**
*
*/
public class HestonVolatilityFunction extends VolatilityFunctionProvider<HestonModelData> {
  /** The FFT pricer */
  private static final FFTPricer FFT_PRICER = new FFTPricer();
  /** The default interpolator */
  private static final Interpolator1D DEFAULT_INTERPOLATOR1D = CombinedInterpolatorExtrapolatorFactory.getInterpolator(DOUBLE_QUADRATIC, FLAT_EXTRAPOLATOR, FLAT_EXTRAPOLATOR);
  /** The default limit of sigma */
  private static final double DEFAULT_LIMIT_SIGMA = 0.3;
  /** The default limit of alpha */
  private static final double DEFAULT_ALPHA = -0.5;

  /** The limit of sigma */
  private final double _limitSigma;
  /** Alpha */
  private final double _alpha;
  /** The limit tolerance */
  private final double _limitTolerance;
  /** The interpolator */
  private final Interpolator1D _interpolator;

  /**
   * Default constructor setting sigma, alpha, the limit tolerance and the interpolator to the default values
   */
  public HestonVolatilityFunction() {
    _limitSigma = DEFAULT_LIMIT_SIGMA;
    _alpha = DEFAULT_ALPHA;
    _limitTolerance = 1e-8;
    _interpolator = DEFAULT_INTERPOLATOR1D;
  }

  /**
   * {@inheritDoc}
   * Only use this for testing. If you have a set of options with the same expiry but different strikes, use #getVolatilitySetFunction
   */
  @Override
  public Function1D<HestonModelData, Double> getVolatilityFunction(final EuropeanVanillaOption option, final double forward) {

    final Function1D<HestonModelData, double[]> func = getVolatilityFunction(forward, new double[] {option.getStrike() }, option.getTimeToExpiry());

    return new Function1D<HestonModelData, Double>() {

      @Override
      public Double evaluate(final HestonModelData x) {
        return func.evaluate(x)[0];
      }
    };
  }

  @Override
  public Function1D<HestonModelData, double[]> getVolatilityFunction(final double forward, final double[] strikes, final double timeToExpiry) {

    final int n = strikes.length;
    final double lowestStrike = strikes[0];
    final double highestStrike = strikes[n - 1];

    return new Function1D<HestonModelData, double[]>() {

      @SuppressWarnings("synthetic-access")
      @Override
      public double[] evaluate(final HestonModelData x) {
        final MartingaleCharacteristicExponent ce = new HestonCharacteristicExponent(x);
        //TODO calculations relating to the FFT setup are made each call, even though they will be very similar (depends on Characteristic
        // Exponent). Maybe worth calculating a typical setup, outside of this function
        final double[][] strikeNPrice = FFT_PRICER.price(forward, 1.0, timeToExpiry, true, ce, lowestStrike, highestStrike, n, _limitSigma, _alpha, _limitTolerance);
        final int m = strikeNPrice.length;
        final double[] k = new double[m];
        final double[] vol = new double[m];
        int count = 0;
        for (int i = 0; i < m; i++) {
          final double strike = strikeNPrice[i][0];
          final double price = strikeNPrice[i][1];
          if (price > 0.0) {
            double impVol;
            try {
              impVol = BlackFormulaRepository.impliedVolatility(price, forward, strike, timeToExpiry, true);
              k[count] = strike;
              vol[count] = impVol;
              count++;
            } catch (final IllegalArgumentException e) {

              //impVol = BlackFormulaRepository.impliedVolatility(price, forward, strike, timeToExpiry, true);
            }
          }
        }
        final double[] res = new double[n];
        if (count == 0) { //i.e. every single price is invalid, which could happen with extreme parameters. All we can do without stopping the
          // fitter, is return zero vols.
          for (int i = 0; i < n; i++) {
            res[i] = 0.0;
          }
        } else {
          double[] validStrikes = new double[count];
          double[] validVols = new double[count];
          if (count == m) {
            validStrikes = k;
            validVols = vol;
          } else {
            validStrikes = Arrays.copyOfRange(k, 0, count);
            validVols = Arrays.copyOfRange(vol, 0, count);
          }
          final Interpolator1DDataBundle dataBundle = _interpolator.getDataBundleFromSortedArrays(validStrikes, validVols);
          for (int i = 0; i < n; i++) {
            res[i] = _interpolator.interpolate(dataBundle, strikes[i]);
          }
        }
        return res;
      }
    };
  }

  /**
   * Calculates the volatility given Heston model parameters, market data and option data
   * @param forward The forward
   * @param strike The strike
   * @param timeToExpiry The time to expiry
   * @param kappa kappa
   * @param theta theta
   * @param vol0 initial volatility
   * @param omega omega
   * @param rho rho
   * @return The volatility
   */
  @ExternalFunction
  public double getVolatility(final double forward, final double strike, final double timeToExpiry, final double kappa, final double theta, final double vol0, final double omega,
      final double rho) {
    final Function1D<HestonModelData, Double> func = getVolatilityFunction(new EuropeanVanillaOption(strike, timeToExpiry, true), forward);
    final HestonModelData data = new HestonModelData(kappa, theta, vol0, omega, rho);
    return func.evaluate(data);
  }

  /**
   * Calculates the volatility given Heston model parameters, market data and an array of strikes
   * @param forward The forward
   * @param strikes The strikes
   * @param timeToExpiry The time to expiry
   * @param kappa kappa
   * @param theta theta
   * @param vol0 initial volatility
   * @param omega omega
   * @param rho rho
   * @return The volatility
   */
  @ExternalFunction
  public double[] getVolatilitySet(final double forward, final double[] strikes, final double timeToExpiry, final double kappa, final double theta, final double vol0, final double omega,
      final double rho) {
    final Function1D<HestonModelData, double[]> func = getVolatilityFunction(forward, strikes, timeToExpiry);
    final HestonModelData data = new HestonModelData(kappa, theta, vol0, omega, rho);
    return func.evaluate(data);
  }

  @Override
  public Function1D<HestonModelData, double[]> getVolatilityAdjointFunction(final EuropeanVanillaOption option, final double forward) {

    final Function1D<HestonModelData, double[][]> func = getVolatilityAdjointFunction(forward, new double[] {option.getStrike() }, option.getTimeToExpiry());
    return new Function1D<HestonModelData, double[]>() {

      @Override
      public double[] evaluate(final HestonModelData x) {
        final double[][] temp = func.evaluate(x);
        Validate.isTrue(temp.length == 1);
        return temp[0];
      }

    };
  }

  @Override
  public Function1D<HestonModelData, double[][]> getVolatilityAdjointFunction(final double forward, final double[] strikes,
      final double timeToExpiry) {

    final FFTModelGreeks greekCal = new FFTModelGreeks();
    final int n = strikes.length;
    final double lowestStrike = strikes[0];
    final double highestStrike = strikes[n - 1];
    final double[][] nodeSense = new double[n][];

    return new Function1D<HestonModelData, double[][]>() {

      @SuppressWarnings("synthetic-access")
      @Override
      public double[][] evaluate(final HestonModelData x) {
        final MartingaleCharacteristicExponent ce = new HestonCharacteristicExponent(x);
        final double[][] greeks = greekCal.getGreeks(forward, 1.0, timeToExpiry, true, ce, lowestStrike, highestStrike, n, _limitSigma, _alpha, _limitTolerance);
        //1st array is strikes and the second is prices (which we don't need)
        final double[] k = greeks[0];
        final double[] prices = greeks[1];
        final int m = k.length;
        final double[] vols = new double[m];
        final double[] vega = new double[m];

        for (int i = 0; i < m; i++) {
          vols[i] = BlackFormulaRepository.impliedVolatility(prices[i], forward, k[i], timeToExpiry, true);
        }
        for (int i = 0; i < m; i++) {
          vega[i] = BlackFormulaRepository.vega(forward, k[i], timeToExpiry, vols[i]);
        }

        final Interpolator1DDataBundle dataBundle = _interpolator.getDataBundleFromSortedArrays(k, vols);

        for (int i = 0; i < n; i++) {
          nodeSense[i] = _interpolator.getNodeSensitivitiesForValue(dataBundle, strikes[i]);
        }

        final int p = greeks.length;

        final double[][] volSense = new double[p - 2][m];
        for (int index = 0; index < p - 2; index++) {
          for (int i = 0; i < m; i++) {
            volSense[index][i] = greeks[index + 2][i] / vega[i]; //TODO here is where vega = 0 -> infinity
          }
        }

        //fake the price, forward, and strike sense since we don't used them
        final double[][] res = new double[n][p + 1];
        for (int index = 0; index < p - 2; index++) {
          final double[] temp = volSense[index];
          for (int i = 0; i < n; i++) {
            final double[] tns = nodeSense[i];
            double sum = 0.0;
            for (int j = 0; j < m; j++) {
              sum += temp[j] * tns[j];
            }
            res[i][index + 3] = sum;
          }
        }

        return res;
      }
    };
  }

  @Override
  public Function1D<HestonModelData, double[]> getModelAdjointFunction(final EuropeanVanillaOption option, final double forward) {

    final Function1D<HestonModelData, double[][]> func = getModelAdjointFunction(forward, new double[] {option.getStrike() }, option.getTimeToExpiry());
    return new Function1D<HestonModelData, double[]>() {

      @Override
      public double[] evaluate(final HestonModelData x) {
        final double[][] temp = func.evaluate(x);
        Validate.isTrue(temp.length == 1);
        return temp[0];
      }

    };
  }

  @Override
  public Function1D<HestonModelData, double[][]> getModelAdjointFunction(final double forward, final double[] strikes,
      final double timeToExpiry) {
    final FFTModelGreeks greekCal = new FFTModelGreeks();
    final int n = strikes.length;
    final double lowestStrike = strikes[0];
    final double highestStrike = strikes[n - 1];
    final double[][] nodeSense = new double[n][];

    return new Function1D<HestonModelData, double[][]>() {

      @SuppressWarnings("synthetic-access")
      @Override
      public double[][] evaluate(final HestonModelData x) {
        final MartingaleCharacteristicExponent ce = new HestonCharacteristicExponent(x);
        final double[][] greeks = greekCal.getGreeks(forward, 1.0, timeToExpiry, true, ce, lowestStrike, highestStrike, n, _limitSigma, _alpha, _limitTolerance);
        //1st array is strikes and the second is prices (which we don't need)

        final double[] k = greeks[0];
        final double[] prices = greeks[1];
        final int m = k.length;
        final double[] kTemp = new double[m];
        final double[] vols = new double[m];
        final double[] vega = new double[m];
        final double[][] modelGreeks = new double[5][m];
        int count = 0;
        for (int i = 0; i < m; i++) {
          double impVol;
          try {
            impVol = BlackFormulaRepository.impliedVolatility(prices[i], forward, k[i], timeToExpiry, true);
            vols[count] = impVol;
            vega[count] = BlackFormulaRepository.vega(forward, k[i], timeToExpiry, impVol);
            kTemp[count] = k[i];
            for (int j = 0; j < 5; j++) {
              modelGreeks[j][count] = greeks[j + 2][i];
            }
            count++;
          } catch (final IllegalArgumentException e) {
            //do nothing
          }
        }

        double[] validStrikes = new double[count];
        double[] validVols = new double[count];
        double[] validVegas = new double[count];
        double[][] validModelGreeks = new double[5][count];
        if (count == m) {
          validStrikes = kTemp;
          validVols = vols;
          validVegas = vega;
          validModelGreeks = modelGreeks;
        } else {
          validStrikes = Arrays.copyOfRange(k, 0, count);
          validVols = Arrays.copyOfRange(vols, 0, count);
          validVegas = Arrays.copyOfRange(vega, 0, count);
          for (int j = 0; j < 5; j++) {
            validModelGreeks[j] = Arrays.copyOfRange(modelGreeks[j], 0, count);
          }
        }

        final Interpolator1DDataBundle dataBundle = _interpolator.getDataBundleFromSortedArrays(validStrikes, validVols);

        for (int i = 0; i < n; i++) {
          nodeSense[i] = _interpolator.getNodeSensitivitiesForValue(dataBundle, strikes[i]);
        }

        final int p = modelGreeks.length;

        final double[][] volSense = new double[p][count];
        for (int index = 0; index < p; index++) {
          for (int i = 0; i < count; i++) {
            volSense[index][i] = validModelGreeks[index][i] / validVegas[i];
          }
        }

        final double[][] res = new double[n][p];
        for (int index = 0; index < p; index++) {
          final double[] temp = volSense[index];
          for (int i = 0; i < n; i++) {
            final double[] tns = nodeSense[i];
            double sum = 0.0;
            for (int j = 0; j < count; j++) {
              sum += temp[j] * tns[j];
            }
            res[i][index] = sum;
          }
        }

        return res;
      }
    };
  }

  @Override
  public int hashCode() {
    return toString().hashCode();
  }

  @Override
  public boolean equals(final Object obj) {
    if (obj == null) {
      return false;
    }
    if (this == obj) {
      return true;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    return true;
  }

  @Override
  public String toString() {
    return "Heston";
  }
}
TOP

Related Classes of com.opengamma.analytics.financial.model.volatility.smile.function.HestonVolatilityFunction

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.