DoubleBarrierOptionSabrModelBoundary.java
package net.finmath.finitedifference.assetderivativevaluation.boundaries;
import net.finmath.finitedifference.assetderivativevaluation.models.FDMSabrModel;
import net.finmath.finitedifference.assetderivativevaluation.products.DoubleBarrierOption;
import net.finmath.finitedifference.assetderivativevaluation.products.FiniteDifferenceEquityProduct;
import net.finmath.finitedifference.boundaries.BoundaryCondition;
import net.finmath.finitedifference.boundaries.StandardBoundaryCondition;
/**
* Boundary conditions for {@link DoubleBarrierOption} under the
* {@link FDMSabrModel}.
*
* <p>
* State variables are assumed to be (S, alpha), where S is the asset level and
* alpha the stochastic volatility factor.
* Dirichlet conditions are imposed in the asset direction, while the
* alpha-direction boundaries are left untouched via
* {@link StandardBoundaryCondition#none()}.
* </p>
*
* <p>
* The double barriers themselves are enforced internally by the product through
* state constraints. Therefore, the outer-domain boundaries use the same
* vanilla asymptotics as {@code EuropeanOption}.
* </p>
*
* <p>
* This implementation uses grid-aware asset-direction boundary values: instead
* of applying asymptotic formulas blindly, it evaluates discounted intrinsic
* value at the actual boundary stock level.
* </p>
*
* @author Alessandro Gnoatto
*/
public class DoubleBarrierOptionSabrModelBoundary implements FiniteDifferenceBoundary {
/**
* The epsilon.
*/
private static final double EPSILON = 1E-6;
/**
* The model.
*/
private final FDMSabrModel model;
/**
* Performs the operation.
*
* @param model The value.
*/
public DoubleBarrierOptionSabrModelBoundary(final FDMSabrModel model) {
this.model = model;
}
@Override
public BoundaryCondition[] getBoundaryConditionsAtLowerBoundary(
final FiniteDifferenceEquityProduct product,
double time,
final double... stateVariables) {
final DoubleBarrierOption option = (DoubleBarrierOption) product;
time = Math.max(time, EPSILON);
final double s = stateVariables.length > 0 ? stateVariables[0] : 0.0;
final BoundaryCondition[] result = new BoundaryCondition[2];
result[0] = StandardBoundaryCondition.dirichlet(
getDiscountedIntrinsicValue(option, time, s)
);
// alpha -> lower boundary: leave PDE row intact
result[1] = StandardBoundaryCondition.none();
return result;
}
@Override
public BoundaryCondition[] getBoundaryConditionsAtUpperBoundary(
final FiniteDifferenceEquityProduct product,
double time,
final double... stateVariables) {
final DoubleBarrierOption option = (DoubleBarrierOption) product;
time = Math.max(time, EPSILON);
final double s = stateVariables.length > 0 ? stateVariables[0] : 0.0;
final BoundaryCondition[] result = new BoundaryCondition[2];
result[0] = StandardBoundaryCondition.dirichlet(
getDiscountedIntrinsicValue(option, time, s)
);
// alpha -> upper boundary: leave PDE row intact
result[1] = StandardBoundaryCondition.none();
return result;
}
private double getDiscountedIntrinsicValue(
final DoubleBarrierOption option,
final double evaluationTime,
final double stock) {
final double maturity = option.getMaturity();
final double tau = Math.max(maturity - evaluationTime, 0.0);
final double discountFactorRiskFree =
model.getRiskFreeCurve().getDiscountFactor(tau);
final double discountFactorDividend =
model.getDividendYieldCurve().getDiscountFactor(tau);
final double discountedForwardLikeSpot = stock * discountFactorDividend;
final double discountedStrike = option.getStrike() * discountFactorRiskFree;
switch (option.getCallOrPut()) {
case CALL:
return Math.max(discountedForwardLikeSpot - discountedStrike, 0.0);
case PUT:
return Math.max(discountedStrike - discountedForwardLikeSpot, 0.0);
default:
throw new IllegalArgumentException("Unsupported option type.");
}
}
}