AmericanOptionSabrModelBoundary.java
package net.finmath.finitedifference.assetderivativevaluation.boundaries;
import net.finmath.finitedifference.assetderivativevaluation.models.FDMSabrModel;
import net.finmath.finitedifference.assetderivativevaluation.products.AmericanOption;
import net.finmath.finitedifference.assetderivativevaluation.products.FiniteDifferenceEquityProduct;
import net.finmath.finitedifference.boundaries.BoundaryCondition;
import net.finmath.finitedifference.boundaries.StandardBoundaryCondition;
import net.finmath.modelling.products.CallOrPut;
/**
* Boundary conditions for {@link AmericanOption} under the {@link
* FDMSabrModel}.
*
* <p>
* State variables are assumed to be {@code (S, alpha)}, where {@code S} is the
* asset level and {@code alpha} the stochastic volatility factor.
* Dirichlet conditions are imposed in the asset direction, while the
* volatility-direction boundaries are left untouched via
* {@link StandardBoundaryCondition#none()}.
* </p>
*
* @author Alessandro Gnoatto
*/
public class AmericanOptionSabrModelBoundary
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 AmericanOptionSabrModelBoundary(final FDMSabrModel model) {
this.model = model;
}
@Override
public BoundaryCondition[] getBoundaryConditionsAtLowerBoundary(
final FiniteDifferenceEquityProduct product,
double time,
final double... stateVariables) {
final AmericanOption option = (AmericanOption) product;
final CallOrPut sign = option.getCallOrPut();
time = Math.max(time, EPSILON);
final double discountFactorRiskFree = model.getRiskFreeCurve().getDiscountFactor(time);
final double riskFreeRate = -Math.log(discountFactorRiskFree) / time;
final double strike = option.getStrike();
final double maturity = option.getMaturity();
final BoundaryCondition[] result = new BoundaryCondition[2];
// S -> 0
if (sign == CallOrPut.CALL) {
result[0] = StandardBoundaryCondition.dirichlet(0.0);
} else {
result[0] = StandardBoundaryCondition.dirichlet(
strike * Math.exp(-riskFreeRate * (maturity - time))
);
}
// 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 AmericanOption option = (AmericanOption) product;
final CallOrPut sign = option.getCallOrPut();
time = Math.max(time, EPSILON);
final double discountFactorRiskFree = model.getRiskFreeCurve().getDiscountFactor(time);
final double riskFreeRate = -Math.log(discountFactorRiskFree) / time;
final double discountFactorDividend = model.getDividendYieldCurve().getDiscountFactor(time);
final double dividendYieldRate = -Math.log(discountFactorDividend) / time;
final double strike = option.getStrike();
final double maturity = option.getMaturity();
final double stock = stateVariables.length > 0 ? stateVariables[0] : 0.0;
final BoundaryCondition[] result = new BoundaryCondition[2];
// S -> infinity
if (sign == CallOrPut.CALL) {
final double dividendAdjustedStockPrice =
stock * Math.exp(-dividendYieldRate * (maturity - time));
result[0] = StandardBoundaryCondition.dirichlet(
dividendAdjustedStockPrice
- strike * Math.exp(-riskFreeRate * (maturity - time))
);
} else {
result[0] = StandardBoundaryCondition.dirichlet(0.0);
}
// alpha -> upper boundary: leave PDE row intact
result[1] = StandardBoundaryCondition.none();
return result;
}
}