com.opengamma.analytics.financial.model.option.pricing.tree.BinomialTreeBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.model.option.pricing.tree.BinomialTreeBuilder.java

Source

/**
 * 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.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.option.definition.OptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.OptionExerciseFunction;
import com.opengamma.analytics.financial.model.option.definition.OptionPayoffFunction;
import com.opengamma.analytics.financial.model.option.definition.StandardOptionDataBundle;
import com.opengamma.analytics.financial.model.tree.RecombiningBinomialTree;
import com.opengamma.util.tuple.DoublesPair;

/**
 * Builds a binomial tree
 * @param <T> StandardOptionDataBundle
 */
public abstract class BinomialTreeBuilder<T extends StandardOptionDataBundle> {

    /**
     * Builds a tree of an asset prices
     * @param maturity The time span (in years) of the tree
     * @param data OptionDataBundle
     * @param nSteps The number of steps in the tree (need at least 1 step)
     * @return tree of an asset prices
     */
    @SuppressWarnings("unchecked")
    public RecombiningBinomialTree<BinomialTreeNode<Double>> buildAssetTree(final double maturity, final T data,
            final int nSteps) {

        final BinomialTreeNode<Double>[][] tree = new BinomialTreeNode[nSteps + 1][];
        double t = 0;
        final double spot = data.getSpot();
        final double dt = maturity / nSteps;

        double[] spots = new double[1];
        spots[0] = spot;

        for (int i = 1; i <= nSteps; i++) {

            t = (i - 1) * dt;
            final double[] forwards = getForwards(spots, data, t, dt);
            final double[] nodes = new double[i + 1];
            int jPlus;
            int jMinus;

            if (i % 2 == 0) { // central node set equal to spot
                final int k = i / 2;
                jPlus = k + 1;
                jMinus = k - 1;
                nodes[k] = spot; // TODO have an option for the centre node to follow the forward rather than the spot
            } else {
                final int k = (i - 1) / 2;
                jPlus = k + 2;
                jMinus = k - 1;
                final double sigma = data.getVolatility(t, spots[k]);
                final DoublesPair nodePair = getCentralNodePair(dt, sigma, forwards[k], spot);
                nodes[k] = nodePair.first;
                nodes[k + 1] = nodePair.second;
            }

            for (int j = jPlus; j <= i; j++) {
                final double sigma = data.getVolatility(t, spots[j - 1]);
                nodes[j] = getNextHigherNode(dt, sigma, forwards[j - 1], nodes[j - 1]);
            }

            for (int j = jMinus; j >= 0; j--) {
                final double sigma = data.getVolatility(t, spots[j]);
                nodes[j] = getNextLowerNode(dt, sigma, forwards[j], nodes[j + 1]);
            }

            tree[i - 1] = new BinomialTreeNode[i];

            for (int j = 0; j < i; j++) {
                final double diff = nodes[j + 1] - nodes[j];
                double p;
                if (diff == 0.0) {
                    // some branches of the tree are stuck at spot = 0.0 - this is not a problem as such
                    Validate.isTrue(Double.doubleToLongBits(forwards[j]) == Double.doubleToLongBits(nodes[j]),
                            "inconsistent nodes");
                    p = 0.5; // Arbitrary as nodes are degenerate
                } else {
                    p = (forwards[j] - nodes[j]) / diff;
                }
                tree[i - 1][j] = new BinomialTreeNode<>(spots[j], p);
            }
            spots = nodes;
        }

        // fill out the final column of nodes - probability is set to zero
        tree[nSteps] = new BinomialTreeNode[nSteps + 1];
        for (int j = 0; j <= nSteps; j++) {
            tree[nSteps][j] = new BinomialTreeNode<>(spots[j], 0.0);
        }

        return new RecombiningBinomialTree<>(tree);
    }

    protected abstract double[] getForwards(final double[] spots, final T data, final double t, final double dt);

    protected abstract double getNextHigherNode(final double dt, final double sigma, final double forward,
            final double lowerNode);

    protected abstract double getNextLowerNode(final double dt, final double sigma, final double forward,
            final double higherNode);

    protected abstract DoublesPair getCentralNodePair(final double dt, final double sigma, final double forward,
            final double centreLevel);

    /**
     * Builds a tree of option prices
     * @param definition Option Definition
     * @param data OptionDataBundle
     * @param assetTree A previously built asset price tree
     * @return a tree of option prices
     */
    @SuppressWarnings("unchecked")
    public RecombiningBinomialTree<BinomialTreeNode<Double>> buildOptionPriceTree(final OptionDefinition definition,
            final T data, final RecombiningBinomialTree<BinomialTreeNode<Double>> assetTree) {

        final int nSteps = assetTree.getDepth() - 1;
        final BinomialTreeNode<Double>[][] tree = new BinomialTreeNode[nSteps + 1][];
        final OptionPayoffFunction<T> payoffFunction = definition.getPayoffFunction();
        final OptionExerciseFunction<T> exerciseFunction = definition.getExerciseFunction();
        final YieldAndDiscountCurve yieldCurve = data.getInterestRateCurve();

        double spot;

        tree[nSteps] = new BinomialTreeNode[nSteps + 1];
        for (int j = 0; j <= nSteps; j++) {
            spot = assetTree.getNode(nSteps, j).getValue();
            final double value = payoffFunction.getPayoff((T) data.withSpot(spot), 0.0);
            tree[nSteps][j] = new BinomialTreeNode<>(value, 0.0); // no need to set the probabilities
        }

        final double maturity = definition.getTimeToExpiry(data.getDate());
        final double dt = maturity / nSteps;
        double t = maturity;
        for (int i = nSteps - 1; i >= 0; i--) {
            t -= dt;
            final double df = yieldCurve.getDiscountFactor(t + dt) / yieldCurve.getDiscountFactor(t);
            tree[i] = new BinomialTreeNode[i + 1];
            for (int j = 0; j <= i; j++) {
                final BinomialTreeNode<Double> node = assetTree.getNode(i, j);
                final double p = node.getUpProbability();
                spot = node.getValue();
                final double optionValue = df
                        * (p * tree[i + 1][j + 1].getValue() + (1 - p) * tree[i + 1][j].getValue());

                final T newData = (T) data.withSpot(spot);
                final double value = exerciseFunction.shouldExercise(newData, optionValue)
                        ? payoffFunction.getPayoff(newData, optionValue)
                        : optionValue;
                tree[i][j] = new BinomialTreeNode<>(value, 0.0);
            }
        }

        return new RecombiningBinomialTree<>(tree);
    }

}