/**
* Copyright (C) 2009 - 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.Validate;
import com.opengamma.analytics.financial.model.option.definition.GeneralNormalOptionDataBundle;
import com.opengamma.util.tuple.DoublesPair;
/**
* Builds a binomial tree where the nodes are set to locally match a normal process. The process that the tree is emulating is of the form df = a(f,t)dt + b(f,t)dw.
* From a node at (f,t) the two daughter nodes f+ and f- (at time t + dt) are set such that p*(1-p)*(f+ - f-)^2 = dt*b(f,t)^2, where p is the probability of reaching f+ from f.
* The forwarding condition is p*f+ + (1-p)*f- = f + a(f,t)*dt. This is adapted from the paper Derman and Kani, The Volatility Smile and Its Implied Tree.
* @param <T> A GeneralNormalOptionDataBundle or anything that extends it
*/
public class NormalBinomialTreeBuilder<T extends GeneralNormalOptionDataBundle> extends BinomialTreeBuilder<T> {
@Override
protected DoublesPair getCentralNodePair(final double dt, final double sigma, final double forward, final double centreLevel) {
final double sigma2dt = sigma * sigma * dt;
final double b = 2 * centreLevel;
final double c = forward * (2 * centreLevel - forward) - sigma2dt;
final double root = b * b - 4 * c;
Validate.isTrue(root >= 0, "can't find upper node - root negative");
final double upper = (b + Math.sqrt(root)) / 2;
final double lower = 2 * centreLevel - upper;
return DoublesPair.of(lower, upper);
}
@Override
protected double getNextHigherNode(final double dt, final double sigma, final double forward, final double lowerNode) {
final double sigma2dt = sigma * sigma * dt;
Validate.isTrue(forward > lowerNode, "need forward > lowerNode");
return sigma2dt / (forward - lowerNode) + forward;
}
@Override
protected double getNextLowerNode(final double dt, final double sigma, final double forward, final double higherNode) {
if (forward == 0.0) {
return 0.0;
}
final double sigma2dt = sigma * sigma * dt;
Validate.isTrue(higherNode > forward, "need higherNode > forward");
double lowerNode = sigma2dt / (forward - higherNode) + forward;
if (lowerNode < 0.0) {
lowerNode = 0.0; // set zero as an absorbing boundary
}
return lowerNode;
}
@Override
protected double[] getForwards(final double[] spots, final T data, final double t, final double dt) {
final int n = spots.length;
final double[] forwards = new double[n];
for (int i = 0; i < n; i++) {
final double drift = data.getLocalDrift(spots[i], t);
forwards[i] = spots[i] + drift * dt;
}
return forwards;
}
}