/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.option.pricing.tree;
import org.apache.commons.lang.NotImplementedException;
import com.google.common.primitives.Doubles;
import com.opengamma.util.ArgumentChecker;
/**
* European type of double barrier option
*/
public class DoubleBarrierOptionFunctionProvider extends BarrierOptionFunctionProvider {
private double _upperBarrier;
private CrossBarrierChecker _checkerDouble;
/**
* @param strike Strike price
* @param timeToExpiry Time to expiry
* @param steps Number of steps
* @param isCall True if call, false if put
* @param lowerBarrier Lower barrier price
* @param upperBarrier Upper barrier price
* @param typeName {@link BarrierTypes}, DownAndOut or UpAndOut
*/
public DoubleBarrierOptionFunctionProvider(final double strike, final double timeToExpiry, final int steps, final boolean isCall, final double lowerBarrier, final double upperBarrier,
final BarrierTypes typeName) {
super(strike, timeToExpiry, steps, isCall, lowerBarrier, BarrierOptionFunctionProvider.BarrierTypes.DownAndOut);
ArgumentChecker.isTrue(upperBarrier > 0., "upperBarrier should be positive");
ArgumentChecker.isTrue(Doubles.isFinite(upperBarrier), "upperBarrier should be finite");
ArgumentChecker.isTrue(upperBarrier > lowerBarrier, "upperBarrier should be greater than lowerBarrier");
switch (typeName) {
case DoubleKnockOut:
_checkerDouble = new DoubleBarrier();
break;
default:
throw new NotImplementedException();
}
_upperBarrier = upperBarrier;
}
@Override
public double[] getPayoffAtExpiry(final double assetPrice, final double downFactor, final double upOverDown) {
final double strike = getStrike();
final int nSteps = getNumberOfSteps();
final int nStepsP = nSteps + 1;
final double sign = getSign();
final double[] values = new double[nStepsP];
double priceTmp = assetPrice * Math.pow(downFactor, nSteps);
for (int i = 0; i < nStepsP; ++i) {
values[i] = _checkerDouble.checkOut(priceTmp) ? 0. : Math.max(sign * (priceTmp - strike), 0.);
priceTmp *= upOverDown;
}
return values;
}
@Override
public double[] getNextOptionValues(final double discount, final double upProbability, final double downProbability, final double[] values, final double baseAssetPrice, final double sumCashDiv,
final double downFactor, final double upOverDown, final int steps) {
final int nStepsP = steps + 1;
final double[] res = new double[nStepsP];
double assetPrice = baseAssetPrice * Math.pow(downFactor, steps);
for (int j = 0; j < nStepsP; ++j) {
res[j] = _checkerDouble.checkOut(assetPrice + sumCashDiv) ? 0. : discount * (upProbability * values[j + 1] + downProbability * values[j]);
assetPrice *= upOverDown;
}
return res;
}
@Override
public double[] getPayoffAtExpiryTrinomial(final double assetPrice, final double downFactor, final double middleOverDown) {
final double strike = getStrike();
final int nSteps = getNumberOfSteps();
final int nNodes = 2 * getNumberOfSteps() + 1;
final double sign = getSign();
final double[] values = new double[nNodes];
double priceTmp = assetPrice * Math.pow(downFactor, nSteps);
for (int i = 0; i < nNodes; ++i) {
values[i] = _checkerDouble.checkOut(priceTmp) ? 0. : Math.max(sign * (priceTmp - strike), 0.);
priceTmp *= middleOverDown;
}
return values;
}
@Override
public double[] getNextOptionValues(final double discount, final double upProbability, final double middleProbability, final double downProbability, final double[] values,
final double baseAssetPrice, final double sumCashDiv, final double downFactor, final double middleOverDown, final int steps) {
final int nNodes = 2 * steps + 1;
final double[] res = new double[nNodes];
double assetPrice = baseAssetPrice * Math.pow(downFactor, steps);
for (int j = 0; j < nNodes; ++j) {
res[j] = _checkerDouble.checkOut(assetPrice + sumCashDiv) ? 0. : discount * (upProbability * values[j + 2] + middleProbability * values[j + 1] + downProbability * values[j]);
assetPrice *= middleOverDown;
}
return res;
}
@Override
public CrossBarrierChecker getChecker() {
return this._checkerDouble;
}
public BarrierTypes getBarrierType() {
return BarrierTypes.DoubleKnockOut;
}
private CrossBarrierChecker getSuperclassChecker() {
return super.getChecker();
}
/**
* Access lower barrier
* @return _barrier in superclass
*/
public double getLowerBarrier() {
return super.getBarrier();
}
/**
* Access upper barrier
* @return _upperBarrier
*/
public double getUpperBarrier() {
return _upperBarrier;
}
@Override
public double getBarrier() {
throw new IllegalArgumentException("Specify lower barrier or upper barrier");
}
/**
* The inherited class checks barriers crossing for double knock-out option
*/
@SuppressWarnings("synthetic-access")
protected class DoubleBarrier extends CrossBarrierChecker {
@Override
public boolean checkOut(final double priceTmp) {
return priceTmp >= _upperBarrier || getSuperclassChecker().checkOut(priceTmp);
}
@Override
public boolean checkStrikeBehindBarrier() {
return getSign() == 1. ? (_upperBarrier <= getStrike()) : false || getSuperclassChecker().checkStrikeBehindBarrier();
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
long temp;
temp = Double.doubleToLongBits(_upperBarrier);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof DoubleBarrierOptionFunctionProvider)) {
return false;
}
DoubleBarrierOptionFunctionProvider other = (DoubleBarrierOptionFunctionProvider) obj;
if (Double.doubleToLongBits(_upperBarrier) != Double.doubleToLongBits(other._upperBarrier)) {
return false;
}
return true;
}
}