BondHullWhiteModelBoundary.java
package net.finmath.finitedifference.interestrate.boundaries;
import net.finmath.finitedifference.boundaries.BoundaryCondition;
import net.finmath.finitedifference.boundaries.StandardBoundaryCondition;
import net.finmath.finitedifference.interestrate.models.FDMHullWhiteModel;
import net.finmath.finitedifference.interestrate.products.Bond;
import net.finmath.finitedifference.interestrate.products.FiniteDifferenceInterestRateProduct;
/**
* Exact boundary conditions for {@link Bond} under {@link FDMHullWhiteModel}.
*
* <p>
* Since the reduced-scope {@link Bond} product consists only of deterministic
* fixed cashflows, its value under Hull-White is known exactly as
* </p>
*
* <p>
* <i>
* V(t,x) = \sum_{i:\,T_i \ge t} C_i P(t,T_i;x),
* </i>
* </p>
*
* <p>
* where {@code C_i} are the remaining deterministic bond cashflows and
* {@code P(t,T_i;x)} is the model discount bond conditional on the current
* state variable {@code x}.
* </p>
*
* <p>
* Hence both lower and upper boundaries can be imposed by exact Dirichlet
* conditions.
* </p>
*
* @author Alessandro Gnoatto
*/
public class BondHullWhiteModelBoundary implements FiniteDifferenceInterestRateBoundary {
/**
* The time tolerance.
*/
private static final double TIME_TOLERANCE = 1E-12;
/**
* The model.
*/
private final FDMHullWhiteModel model;
/**
* Creates the exact Hull-White boundary for deterministic-cashflow bonds.
*
* @param model The Hull-White model.
*/
public BondHullWhiteModelBoundary(final FDMHullWhiteModel model) {
if (model == null) {
throw new IllegalArgumentException("model must not be null.");
}
if (model.getInitialValue() == null || model.getInitialValue().length != 1) {
throw new IllegalArgumentException(
"BondHullWhiteModelBoundary requires a one-dimensional Hull-White model.");
}
this.model = model;
}
@Override
public BoundaryCondition[] getBoundaryConditionsAtLowerBoundary(
final FiniteDifferenceInterestRateProduct product,
final double time,
final double... stateVariables) {
final Bond bond = validateAndCastProduct(product);
validateStateVariables(stateVariables);
return new BoundaryCondition[] {
StandardBoundaryCondition.dirichlet(
getExactBondValue(bond, time, stateVariables[0])
)
};
}
@Override
public BoundaryCondition[] getBoundaryConditionsAtUpperBoundary(
final FiniteDifferenceInterestRateProduct product,
final double time,
final double... stateVariables) {
final Bond bond = validateAndCastProduct(product);
validateStateVariables(stateVariables);
return new BoundaryCondition[] {
StandardBoundaryCondition.dirichlet(
getExactBondValue(bond, time, stateVariables[0])
)
};
}
private Bond validateAndCastProduct(final FiniteDifferenceInterestRateProduct product) {
if (!(product instanceof Bond)) {
throw new IllegalArgumentException(
"BondHullWhiteModelBoundary requires a Bond product.");
}
return (Bond) product;
}
private void validateStateVariables(final double[] stateVariables) {
if (stateVariables == null || stateVariables.length != 1) {
throw new IllegalArgumentException("Exactly one state variable is required.");
}
}
private double getExactBondValue(
final Bond bond,
final double time,
final double stateVariable) {
double value = 0.0;
for (int periodIndex = 0; periodIndex < bond.getSchedule().getNumberOfPeriods(); periodIndex++) {
final double paymentTime = bond.getSchedule().getPayment(periodIndex);
if (paymentTime < time - TIME_TOLERANCE) {
continue;
}
final double cashflow = bond.getCashflow(periodIndex);
final double discountBond = model.getDiscountBond(time, paymentTime, stateVariable);
value += cashflow * discountBond;
}
return value;
}
}