Package com.opengamma.analytics.financial.model.finitedifference.applications

Source Code of com.opengamma.analytics.financial.model.finitedifference.applications.BarrierOptionPricer

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

import org.apache.commons.lang.Validate;

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.DirichletBoundaryCondition;
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.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.option.definition.Barrier;
import com.opengamma.analytics.financial.model.option.definition.Barrier.BarrierType;
import com.opengamma.analytics.financial.model.option.definition.Barrier.KnockType;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackBarrierPriceFunction;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils;
import com.opengamma.analytics.math.function.Function1D;

/**
*
*/
public class BarrierOptionPricer {

  private static final InitialConditionsProvider ICP = new InitialConditionsProvider();
  private static final PDE1DCoefficientsProvider PDE = new PDE1DCoefficientsProvider();
  private static final ThetaMethodFiniteDifference SOLVER = new ThetaMethodFiniteDifference();

  private static final int DEFAULT_XNODES = 100;
  private static final int DEFAULT_TNODES = 50;
  private static final double DEFAULT_LAMBDA = 0.0;
  private static final double DEFAULT_BUNCHING = 1.0;
  private static final double DEFAULT_Z = 2.0;

  private final int _nTNodes;
  private final int _nXNodes;
  private final double _lambda;
  private final double _bunching;
  private final double _z = DEFAULT_Z;

  public BarrierOptionPricer() {
    _nTNodes = DEFAULT_TNODES;
    _nXNodes = DEFAULT_XNODES;
    _lambda = DEFAULT_LAMBDA;
    _bunching = DEFAULT_BUNCHING;
  }

  public BarrierOptionPricer(final int numXNodes, final int numTNodes, final double lambda, final double bunching) {

    _nXNodes = numXNodes;
    _nTNodes = numTNodes;
    _lambda = lambda;
    _bunching = bunching;

  }

  /**
   * Computes the price of a barrier option in the Black world.
   * @param option The underlying European vanilla option.
   * @param barrier The barrier.
   * @param rebate The rebate. This is paid <b>immediately</b> if the knock-out barrier is hit and at expiry if the knock-in barrier is not hit
   * @param spot The spot price.
   * @param costOfCarry The cost of carry (i.e. the forward = spot*exp(costOfCarry*T) )
   * @param rate The interest rate.
   * @param sigma The Black volatility.
   * @return The price.
   */
  public double getPrice(final EuropeanVanillaOption option, final Barrier barrier, final double rebate, final double spot, final double costOfCarry, final double rate, final double sigma) {
    Validate.notNull(option, "option");
    Validate.notNull(barrier, "barrier");
    final boolean isKnockIn = (barrier.getKnockType() == KnockType.IN);
    final boolean isDown = (barrier.getBarrierType() == BarrierType.DOWN);

    //in these pathological cases the barrier is hit immediately so the value is just from the rebate (knock-out) or a European option (knock-in)
    if (isDown && spot <= barrier.getBarrierLevel() || !isDown && spot >= barrier.getBarrierLevel()) {
      if (isKnockIn) {
        return blackPrice(spot, option.getStrike(), option.getTimeToExpiry(), rate, costOfCarry, sigma, option.isCall());
      }
      return rebate;
    }
    if (isKnockIn) {
      return inBarrier(spot, barrier.getBarrierLevel(), option.getStrike(), option.getTimeToExpiry(), rate, costOfCarry, sigma, option.isCall(), rebate);
    }
    return outBarrier(spot, barrier.getBarrierLevel(), option.getStrike(), option.getTimeToExpiry(), rate, costOfCarry, sigma, option.isCall(), rebate);
  }

  /**
   * Computes the price of a one-touch out barrier option in the Black-Scholes world by solving the BS PDE on a finite difference grid. If a barrier is hit at any time before expiry,
   * the option is cancelled (knocked-out) and a rebate (which is often zero) is paid <b>immediately</b>. If the barrier is not hit, then a normal European option payment is made. <p>
   * If the barrier is above the spot it is assumed to be an up-and-out barrier (otherwise it would expire immediately) otherwise it is a down-and-out barrier
   * As there are exact formulae for this case (see {@link BlackBarrierPriceFunction}), this is purely for testing purposes.
   * @param spot The current (i.e. option price time) of the underlying
   * @param barrierLevel The barrier level
   * @param strike The strike of the European option
   * @param expiry The expiry of the option
   * @param rate The interest rate.
   * @param carry The cost-of-carry (i.e. the forward = spot*exp(costOfCarry*T) )
   * @param vol The Black volatility.
   * @param isCall true for call
   * @param rebate The rebate amount.
   * @return The price.
   */
  public double outBarrier(final double spot, final double barrierLevel, final double strike, final double expiry, final double rate, final double carry,
      final double vol, final boolean isCall, final double rebate) {

    final Function1D<Double, Double> intCon = ICP.getEuropeanPayoff(strike, isCall);
    final ConvectionDiffusionPDE1DStandardCoefficients pde = PDE.getBlackScholes(rate, rate - carry, vol);
    final boolean isUp = barrierLevel > spot;

    final double adj = 0.0; // _lambda == 0 ? ZETA * vol * Math.sqrt(expiry / (_nTNodes - 1)) : 0.0;

    double sMin;
    double sMax;
    BoundaryCondition lower;
    BoundaryCondition upper;
    if (isUp) {
      sMin = 0.0;
      sMax = barrierLevel * Math.exp(-adj); //bring the barrier DOWN slightly to adjust for discrete monitoring
      if (isCall) {
        lower = new DirichletBoundaryCondition(0.0, sMin);
      } else {
        final Function1D<Double, Double> lowerValue = new Function1D<Double, Double>() {
          @Override
          public Double evaluate(final Double tau) {
            return Math.exp(-rate * tau) * strike;
          }
        };
        lower = new DirichletBoundaryCondition(lowerValue, sMin);
      }
      upper = new DirichletBoundaryCondition(rebate, sMax);
    } else {
      sMin = barrierLevel * Math.exp(adj); //bring the barrier UP slightly to adjust for discrete monitoring
      sMax = spot * Math.exp(_z * Math.sqrt(expiry));
      lower = new DirichletBoundaryCondition(rebate, sMin);

      if (isCall) {
        final Function1D<Double, Double> upperValue = new Function1D<Double, Double>() {
          @Override
          public Double evaluate(final Double tau) {
            return Math.exp(-rate * tau) * (spot * Math.exp(carry * tau) - strike);
          }
        };
        upper = new DirichletBoundaryCondition(upperValue, sMax);
      } else {
        upper = new DirichletBoundaryCondition(0.0, sMax);
      }
    }

    final MeshingFunction tMesh = new ExponentialMeshing(0, expiry, _nTNodes, _lambda);
    final MeshingFunction xMesh = new HyperbolicMeshing(sMin, sMax, spot, _nXNodes, _bunching);
    final PDEGrid1D grid = new PDEGrid1D(tMesh, xMesh);
    final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> pdeData = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde, intCon, lower, upper, grid);

    final PDEResults1D res = SOLVER.solve(pdeData);
    //for now just do linear interpolation on price. TODO replace this with something more robust
    final double[] xNodes = grid.getSpaceNodes();

    final int index = SurfaceArrayUtils.getLowerBoundIndex(xNodes, spot);
    final double w = (xNodes[index + 1] - spot) / (xNodes[index + 1] - xNodes[index]);
    return w * res.getFunctionValue(index) + (1 - w) * res.getFunctionValue(index + 1);
  }

  /**
   * Computes the price of a one-touch in barrier option in the Black-Scholes world by solving the BS PDE on a finite difference grid. If a barrier is hit at any time before expiry,
   * the option becomes a simple European (call or put). If the barrier is not hit a rebate is paid at the option expiry<p>
   * If the barrier is above the spot it is assumed to be an up-and-in barrier  otherwise it is a down-and-in barrier
   * As there are exact formulae for this case (see {@link BlackBarrierPriceFunction}), this is purely for testing purposes.
   * @param spot The current (i.e. option price time) of the underlying
   * @param barrierLevel The barrier level
   * @param strike The strike of the European option
   * @param expiry The expiry of the option
   * @param rate The interest rate.
   * @param carry The cost-of-carry (i.e. the forward = spot*exp(costOfCarry*T) )
   * @param vol The Black volatility.
   * @param isCall true for call
   * @param rebate The rebate amount.
   * @return The price.
   */
  public double inBarrier(final double spot, final double barrierLevel, final double strike, final double expiry, final double rate, final double carry,
      final double vol, final boolean isCall, final double rebate) {
    final double outPrice = outBarrierSpecial(spot, barrierLevel, strike, expiry, rate, carry, vol, isCall, rebate);
    final double bsPrice = blackPrice(spot, strike, expiry, rate, carry, vol, isCall);
    return bsPrice + Math.exp(-rate * expiry) * rebate - outPrice;
  }

  /**
   * Computes the price of a one-touch out barrier option in the Black-Scholes world assuming the rebate is paid at the option expiry. This is NOT the case for a out barrier,
   * but is for an in, and as we must price an in barrier and a European option plus a bond (the rebate) minus an out, we need this special case.
   * @param spot The current (i.e. option price time) of the underlying
   * @param barrierLevel The barrier level
   * @param strike The strike of the European option
   * @param expiry The expiry of the option
   * @param rate The interest rate.
   * @param carry The cost-of-carry (i.e. the forward = spot*exp(costOfCarry*T) )
   * @param vol The Black volatility.
   * @param isCall true for call
   * @param rebate The rebate amount.
   * @return The price.
   */
  protected double outBarrierSpecial(final double spot, final double barrierLevel, final double strike, final double expiry, final double rate, final double carry,
      final double vol, final boolean isCall, final double rebate) {

    final Function1D<Double, Double> intCon = ICP.getEuropeanPayoff(strike, isCall);
    final ConvectionDiffusionPDE1DStandardCoefficients pde = PDE.getBlackScholes(rate, rate - carry, vol);
    final boolean isUp = barrierLevel > spot;

    final Function1D<Double, Double> rebateValue = new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double tau) {
        return rebate * Math.exp(-rate * tau);
      }
    };

    double sMin;
    double sMax;
    BoundaryCondition lower;
    BoundaryCondition upper;
    if (isUp) {
      sMin = 0.0;
      sMax = barrierLevel;
      if (isCall) {
        lower = new DirichletBoundaryCondition(0.0, sMin);
      } else {
        final Function1D<Double, Double> lowerValue = new Function1D<Double, Double>() {
          @Override
          public Double evaluate(final Double tau) {
            return Math.exp(-rate * tau) * strike;
          }
        };
        lower = new DirichletBoundaryCondition(lowerValue, sMin);
      }
      upper = new DirichletBoundaryCondition(rebateValue, sMax);
    } else {
      sMin = barrierLevel;
      sMax = spot * Math.exp(_z * Math.sqrt(expiry));
      lower = new DirichletBoundaryCondition(rebateValue, sMin);
      if (isCall) {
        final Function1D<Double, Double> upperValue = new Function1D<Double, Double>() {
          @Override
          public Double evaluate(final Double tau) {
            return Math.exp(-rate * tau) * (spot * Math.exp(carry * tau) - strike);
          }
        };
        upper = new DirichletBoundaryCondition(upperValue, sMax);
      } else {
        upper = new DirichletBoundaryCondition(0.0, sMax);
      }
    }

    final MeshingFunction tMesh = new ExponentialMeshing(0, expiry, _nTNodes, _lambda);
    final MeshingFunction xMesh = new HyperbolicMeshing(sMin, sMax, spot, _nXNodes, _bunching);
    final PDEGrid1D grid = new PDEGrid1D(tMesh, xMesh);
    final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> pdeData = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde, intCon, lower, upper, grid);

    final PDEResults1D res = SOLVER.solve(pdeData);
    //for now just do linear interpolation on price. TODO replace this with something more robust
    final double[] xNodes = grid.getSpaceNodes();
    final int index = SurfaceArrayUtils.getLowerBoundIndex(xNodes, spot);
    final double w = (xNodes[index + 1] - spot) / (xNodes[index + 1] - xNodes[index]);
    return w * res.getFunctionValue(index) + (1 - w) * res.getFunctionValue(index + 1);
  }

  protected double blackPrice(final double spot, final double strike, final double expiry, final double rate, final double carry,
      final double vol, final boolean isCall) {

    final Function1D<Double, Double> intCon = ICP.getEuropeanPayoff(strike, isCall);
    final ConvectionDiffusionPDE1DStandardCoefficients pde = PDE.getBlackScholes(rate, rate - carry, vol);

    final double sMin = 0.0;
    final double sMax = spot * Math.exp(_z * Math.sqrt(expiry));
    BoundaryCondition lower;
    BoundaryCondition upper;
    if (isCall) {
      lower = new DirichletBoundaryCondition(0.0, sMin);
      final Function1D<Double, Double> upperValue = new Function1D<Double, Double>() {
        @Override
        public Double evaluate(final Double tau) {
          return Math.exp(-rate * tau) * (spot * Math.exp(carry * tau) - strike);
        }
      };
      upper = new DirichletBoundaryCondition(upperValue, sMax);
    } else {
      final Function1D<Double, Double> lowerValue = new Function1D<Double, Double>() {
        @Override
        public Double evaluate(final Double tau) {
          return Math.exp(-rate * tau) * strike;
        }
      };
      lower = new DirichletBoundaryCondition(lowerValue, sMin);
      upper = new DirichletBoundaryCondition(0.0, sMax);
    }

    final MeshingFunction tMesh = new ExponentialMeshing(0, expiry, _nTNodes, _lambda);
    final MeshingFunction xMesh = new HyperbolicMeshing(sMin, sMax, spot, _nXNodes, _bunching);
    final PDEGrid1D grid = new PDEGrid1D(tMesh, xMesh);
    final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> pdeData = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde, intCon, lower, upper, grid);

    final PDEResults1D res = SOLVER.solve(pdeData);
    //for now just do linear interpolation on price. TODO replace this with something more robust
    final double[] xNodes = grid.getSpaceNodes();
    final int index = SurfaceArrayUtils.getLowerBoundIndex(xNodes, spot);
    final double w = (xNodes[index + 1] - spot) / (xNodes[index + 1] - xNodes[index]);
    return w * res.getFunctionValue(index) + (1 - w) * res.getFunctionValue(index + 1);
  }
}
TOP

Related Classes of com.opengamma.analytics.financial.model.finitedifference.applications.BarrierOptionPricer

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.