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

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

/**
* Copyright (C) 2009 - 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 java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.Validate;

import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.math.matrix.MatrixAlgebra;
import com.opengamma.analytics.math.matrix.OGMatrixAlgebra;
import com.opengamma.util.ArgumentChecker;

/**
*
* @param <T> Type of the data needed for the volatility function
*/
public abstract class VolatilityFunctionProvider<T extends SmileModelData> {

  private static final MatrixAlgebra MA = new OGMatrixAlgebra();

  private static final double EPS = 1e-6;

  // public abstract int getNumberOfParameters();

  /**
   * Returns a function that, given data of type T, calculates the volatility.
   * @param option The option, not null
   * @param forward The forward value of the underlying
   * @return Returns a function that, given data of type T, calculates the volatility
   */
  public abstract Function1D<T, Double> getVolatilityFunction(final EuropeanVanillaOption option, final double forward);

  /**
   * Returns a function that, given data of type T, calculates the volatilities for the given strikes
   * @param forward the forward
   * @param strikes set of strikes
   * @param timeToExpiry time-to-expiry
   * @return A set of volatilities for the given strikes
   */
  public Function1D<T, double[]> getVolatilityFunction(final double forward, final double[] strikes, final double timeToExpiry) {

    final int n = strikes.length;
    final List<Function1D<T, Double>> funcs = new ArrayList<>(n);
    for (int i = 0; i < n; i++) {
      final boolean isCall = strikes[i] >= forward;
      funcs.add(getVolatilityFunction(new EuropeanVanillaOption(strikes[i], timeToExpiry, isCall), forward));
    }

    return new Function1D<T, double[]>() {
      @Override
      public double[] evaluate(final T data) {
        final double[] res = new double[n];
        for (int i = 0; i < n; i++) {
          res[i] = funcs.get(i).evaluate(data);
        }
        return res;
      }

    };
  }

  /**
   * Returns a function  that calculates volatility and the ajoint (volatility sensitivity to forward, strike and model parameters)
   * by means of central finite difference - this should be overridden where possible
   * @param option The option, not null
   * @param forward The forward value of the underlying
   * @return Returns a function that, given data of type T, calculates the volatility adjoint
   */
  public Function1D<T, double[]> getVolatilityAdjointFunction(final EuropeanVanillaOption option, final double forward) {
    Validate.notNull(option, "option");
    Validate.isTrue(forward >= 0.0, "forward must be greater than zero");
    final Function1D<T, Double> func = getVolatilityFunction(option, forward);

    return new Function1D<T, double[]>() {
      @SuppressWarnings("synthetic-access")
      @Override
      public final double[] evaluate(final T data) {
        Validate.notNull(data, "data");
        final double[] x = new double[3 + data.getNumberOfParameters()]; //vol, fwd, strike, the model parameters
        x[0] = func.evaluate(data);
        x[1] = forwardBar(option, forward, data);
        x[2] = strikeBar(option, forward, data);
        System.arraycopy(paramBar(func, data), 0, x, 3, data.getNumberOfParameters());
        return x;
      }
    };
  }

  /**
   *Returns a function  that calculates the volatility sensitivity to model parameters
   * by means of central finite difference - this should be overridden where possible
   * @param option The option, not null
   * @param forward The forward value of the underlying
   * @return Returns a function that, given data of type T, calculates the volatility model sensitivity
   */
  public Function1D<T, double[]> getModelAdjointFunction(final EuropeanVanillaOption option, final double forward) {
    Validate.notNull(option, "option");
    Validate.isTrue(forward >= 0.0, "forward must be greater than zero");
    final Function1D<T, Double> func = getVolatilityFunction(option, forward);

    return new Function1D<T, double[]>() {
      @SuppressWarnings("synthetic-access")
      @Override
      public final double[] evaluate(final T data) {
        Validate.notNull(data, "data");
        return paramBar(func, data);
      }
    };
  }

  /**
   * The volatility adjoint set by finite difference, unless this is overloaded
   * @param forward Forward value of underlying
   * @param strikes The strikes
   * @param timeToExpiry Time-toExpiry
   * @return The first column is is vol at each strike, the second and third are the forward and strike sensitivity at each strike, and the
   * remaining columns are the model parameter sensitivities are each strike
   */
  public Function1D<T, double[][]> getVolatilityAdjointFunction(final double forward, final double[] strikes, final double timeToExpiry) {

    final int n = strikes.length;
    final Function1D<T, double[]> func = getVolatilityFunction(forward, strikes, timeToExpiry);

    return new Function1D<T, double[][]>() {
      @SuppressWarnings("synthetic-access")
      @Override
      public double[][] evaluate(final T data) {
        Validate.notNull(data, "data");
        final double[][] res = new double[3 + data.getNumberOfParameters()][n];
        res[0] = func.evaluate(data);
        res[1] = forwardBar(strikes, timeToExpiry, forward, data);
        res[2] = strikeBar(strikes, timeToExpiry, forward, data);
        final double[][] temp = paramBarSet(func, data);
        final int m = temp.length;
        for (int i = 0; i < m; i++) {
          res[3 + i] = temp[i];
        }
        //now transpose
        //TODO a transpose that works on double[][]?
        return MA.getTranspose(new DoubleMatrix2D(res)).getData();
      }
    };
  }

  protected Function1D<T, double[][]> getVolatilityAdjointFunctionByCallingSingleStrikes(final double forward, final double[] strikes, final double timeToExpiry) {

    final int n = strikes.length;
    final List<Function1D<T, double[]>> funcs = new ArrayList<>(n);
    for (int i = 0; i < n; i++) {
      final boolean isCall = strikes[i] >= forward;
      funcs.add(getVolatilityAdjointFunction(new EuropeanVanillaOption(strikes[i], timeToExpiry, isCall), forward));
    }

    return new Function1D<T, double[][]>() {
      @Override
      public double[][] evaluate(final T data) {
        final double[][] res = new double[n][];
        for (int i = 0; i < n; i++) {
          res[i] = funcs.get(i).evaluate(data);
        }
        return res;
      }

    };
  }

  /**
   * Returns a function  that calculates the volatility sensitivity to model parameters for a range of strikes by means of
   * central finite difference - this should be overridden where possible
   * @param forward Forward value of underlying
   * @param strikes The strikes
   * @param timeToExpiry Time-toExpiry
   * @return  Matrix (i.e. double[][]) of volatility sensitivities to model parameters, where the columns are the model parameter
   *  sensitivities at each strike
   */
  public Function1D<T, double[][]> getModelAdjointFunction(final double forward, final double[] strikes, final double timeToExpiry) {

    final Function1D<T, double[]> func = getVolatilityFunction(forward, strikes, timeToExpiry);

    return new Function1D<T, double[][]>() {
      @SuppressWarnings("synthetic-access")
      @Override
      public double[][] evaluate(final T data) {
        Validate.notNull(data, "data");
        final double[][] temp = paramBarSet(func, data);

        //now transpose
        //TODO a transpose that works on double[][]?
        return MA.getTranspose(new DoubleMatrix2D(temp)).getData();
      }
    };
  }

  protected Function1D<T, double[][]> getModelAdjointFunctionByCallingSingleStrikes(final double forward, final double[] strikes, final double timeToExpiry) {

    final int n = strikes.length;
    final List<Function1D<T, double[]>> funcs = new ArrayList<>(n);
    for (int i = 0; i < n; i++) {
      final boolean isCall = strikes[i] >= forward;
      funcs.add(getModelAdjointFunction(new EuropeanVanillaOption(strikes[i], timeToExpiry, isCall), forward));
    }

    return new Function1D<T, double[][]>() {
      @Override
      public double[][] evaluate(final T data) {
        final double[][] res = new double[n][];
        for (int i = 0; i < n; i++) {
          res[i] = funcs.get(i).evaluate(data);
        }
        return res;
      }

    };
  }

  private double forwardBar(final EuropeanVanillaOption option, final double forward, final T data) {
    final Function1D<T, Double> funcUp = getVolatilityFunction(option, forward + EPS);
    final Function1D<T, Double> funcDown = getVolatilityFunction(option, forward - EPS);
    return (funcUp.evaluate(data) - funcDown.evaluate(data)) / 2 / EPS;
  }

  private double[] forwardBar(final double[] strikes, final double timeToExpiry, final double forward, final T data) {
    final int n = strikes.length;
    final Function1D<T, double[]> funcUp = getVolatilityFunction(forward + EPS, strikes, timeToExpiry);
    final Function1D<T, double[]> funcDown = getVolatilityFunction(forward - EPS, strikes, timeToExpiry);
    final double[] res = new double[n];
    final double[] up = funcUp.evaluate(data);
    final double[] down = funcDown.evaluate(data);
    for (int i = 0; i < n; i++) {
      res[i] = (up[i] - down[i]) / 2 / EPS;
    }
    return res;
  }

  private double strikeBar(final EuropeanVanillaOption option, final double forward, final T data) {
    final Function1D<T, Double> funcUp = getVolatilityFunction(option.withStrike(option.getStrike() + EPS), forward);
    final Function1D<T, Double> funcDown = getVolatilityFunction(option.withStrike(option.getStrike() - EPS), forward);
    return (funcUp.evaluate(data) - funcDown.evaluate(data)) / 2 / EPS;
  }

  private double[] strikeBar(final double[] strikes, final double timeToExpiry, final double forward, final T data) {
    final int n = strikes.length;
    final double[] res = new double[n];
    final double[] strikesUp = new double[n];
    final double[] strikesDown = new double[n];
    for (int i = 0; i < n; i++) {
      strikesUp[i] = strikes[i] + EPS;
    }
    for (int i = 0; i < n; i++) {
      strikesDown[i] = strikes[i] - EPS;
    }
    final Function1D<T, double[]> funcUp = getVolatilityFunction(forward, strikesUp, timeToExpiry);
    final Function1D<T, double[]> funcDown = getVolatilityFunction(forward, strikesDown, timeToExpiry);
    final double[] up = funcUp.evaluate(data);
    final double[] down = funcDown.evaluate(data);
    for (int i = 0; i < n; i++) {
      res[i] = (up[i] - down[i]) / 2 / EPS;
    }

    return res;
  }

  private double[] paramBar(final Function1D<T, Double> func, final T data) {
    final int n = data.getNumberOfParameters();
    final double[] res = new double[n];
    for (int i = 0; i < n; i++) {
      res[i] = paramBar(func, data, i);
    }
    return res;
  }

  /**
   * Get the model's sensitivity to its parameters by finite-difference, taking care on the boundary of allowed regions
   * @param func Function that gives the volatility for a set of model parameters
   * @param data Model parameters
   * @param index the index of the model parameter
   * @return The first derivative of the volatility WRT the parameter given by index
   */
  private double paramBar(final Function1D<T, Double> func, final T data, final int index) {
    final double mid = data.getParameter(index);
    final double up = mid + EPS;
    final double down = mid - EPS;
    if (data.isAllowed(index, down)) {
      if (data.isAllowed(index, up)) {
        final T dUp = (T) data.with(index, up);
        final T dDown = (T) data.with(index, down);
        return (func.evaluate(dUp) - func.evaluate(dDown)) / 2 / EPS;
      }
      final T dDown = (T) data.with(index, down);
      return (func.evaluate(data) - func.evaluate(dDown)) / EPS;
    }
    ArgumentChecker.isTrue(data.isAllowed(index, up), "No values and index {} = {} are allowed", index, mid);
    final T dUp = (T) data.with(index, up);
    return (func.evaluate(dUp) - func.evaluate(data)) / EPS;
  }

  private double[][] paramBarSet(final Function1D<T, double[]> func, final T data) {
    final int n = data.getNumberOfParameters();
    final double[][] res = new double[n][];
    for (int i = 0; i < n; i++) {
      res[i] = paramBarSet(func, data, i);
    }
    return res;
  }

  /**
   * Get the model's sensitivity to its parameters by finite-difference, taking care on the boundary of allowed regions
   * @param func Function that gives the volatility for a set of model parameters
   * @param data Model parameters
   * @param index the index of the model parameter
   * @return The first derivative of the volatility WRT the parameter given by index
   */
  private double[] paramBarSet(final Function1D<T, double[]> func, final T data, final int index) {
    final double mid = data.getParameter(index);
    final double up = mid + EPS;
    final double down = mid - EPS;
    if (data.isAllowed(index, down)) {
      if (data.isAllowed(index, up)) {
        final T dUp = (T) data.with(index, up);
        final T dDown = (T) data.with(index, down);
        final double[] rUp = func.evaluate(dUp);
        final double[] rDown = func.evaluate(dDown);
        final int m = rUp.length;
        final double[] res = new double[m];
        for (int i = 0; i < m; i++) {
          res[i] = (rUp[i] - rDown[i]) / 2 / EPS;
        }
        return res;
      }
      final double[] rMid = func.evaluate(data);
      final double[] rDown = func.evaluate((T) data.with(index, down));
      final int m = rMid.length;
      final double[] res = new double[m];
      for (int i = 0; i < m; i++) {
        res[i] = (rMid[i] - rDown[i]) / 2 / EPS;
      }
      return res;
    }
    ArgumentChecker.isTrue(data.isAllowed(index, up), "No values and index {} = {} are allowed", index, mid);
    final double[] rMid = func.evaluate(data);
    final double[] rUp = func.evaluate((T) data.with(index, up));
    final int m = rMid.length;
    final double[] res = new double[m];
    for (int i = 0; i < m; i++) {
      res[i] = (rUp[i] - rMid[i]) / 2 / EPS;
    }
    return res;
  }

}
TOP

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

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.