TouchOptionHestonModelBoundary.java
package net.finmath.finitedifference.assetderivativevaluation.boundaries;
import net.finmath.finitedifference.assetderivativevaluation.models.FDMHestonModel;
import net.finmath.finitedifference.assetderivativevaluation.products.FiniteDifferenceEquityProduct;
import net.finmath.finitedifference.assetderivativevaluation.products.TouchOption;
import net.finmath.finitedifference.boundaries.BoundaryCondition;
import net.finmath.finitedifference.boundaries.StandardBoundaryCondition;
import net.finmath.modelling.products.BarrierType;
/**
* Boundary conditions for {@link TouchOption} under {@link FDMHestonModel}.
*
* <p>
* State variables are assumed to be (S, v), where S is the asset level and
* v the variance.
* </p>
*
* <p>
* Milestone scope:
* European expiry-settled cash one-touch / no-touch only.
* </p>
*
* <p>
* The barrier is assumed to lie on the spot boundary. The variance-direction
* boundaries are left free via {@link StandardBoundaryCondition#none()}.
* </p>
*
* @author Alessandro Gnoatto
*/
public class TouchOptionHestonModelBoundary implements FiniteDifferenceBoundary {
/**
* The epsilon.
*/
private static final double EPSILON = 1E-6;
/**
* The model.
*/
private final FDMHestonModel model;
/**
* Performs the operation.
*
* @param model The value.
*/
public TouchOptionHestonModelBoundary(final FDMHestonModel model) {
this.model = model;
}
@Override
public BoundaryCondition[] getBoundaryConditionsAtLowerBoundary(
final FiniteDifferenceEquityProduct product,
double time,
final double... stateVariables) {
final TouchOption option = (TouchOption) product;
final BarrierType barrierType = option.getBarrierType();
time = Math.max(time, EPSILON);
final BoundaryCondition[] result = new BoundaryCondition[2];
/*
* Spot lower boundary.
*
* DOWN_OUT no-touch: knocked out -> 0.
* Otherwise (DOWN_IN one-touch, or continuation side of UP_OUT no-
* touch):
* discounted expiry cash.
*/
if (barrierType == BarrierType.DOWN_OUT) {
result[0] = StandardBoundaryCondition.dirichlet(0.0);
} else {
result[0] = StandardBoundaryCondition.dirichlet(
getDiscountedCashValue(option, time)
);
}
/* Variance lower boundary left free. */
result[1] = StandardBoundaryCondition.none();
return result;
}
@Override
public BoundaryCondition[] getBoundaryConditionsAtUpperBoundary(
final FiniteDifferenceEquityProduct product,
double time,
final double... stateVariables) {
final TouchOption option = (TouchOption) product;
final BarrierType barrierType = option.getBarrierType();
time = Math.max(time, EPSILON);
final BoundaryCondition[] result = new BoundaryCondition[2];
/*
* Spot upper boundary.
*
* UP_OUT no-touch: knocked out -> 0.
* Otherwise (UP_IN one-touch, or continuation side of DOWN_OUT no-
* touch):
* discounted expiry cash.
*/
if (barrierType == BarrierType.UP_OUT) {
result[0] = StandardBoundaryCondition.dirichlet(0.0);
} else {
result[0] = StandardBoundaryCondition.dirichlet(
getDiscountedCashValue(option, time)
);
}
/* Variance upper boundary left free. */
result[1] = StandardBoundaryCondition.none();
return result;
}
private double getDiscountedCashValue(final TouchOption option, final double evaluationTime) {
if (evaluationTime >= option.getMaturity()) {
return option.getPayoffAmount();
}
final double dfTime = model.getRiskFreeCurve().getDiscountFactor(evaluationTime);
final double dfMat = model.getRiskFreeCurve().getDiscountFactor(option.getMaturity());
return option.getPayoffAmount() * dfMat / dfTime;
}
}