com.opengamma.analytics.financial.model.volatility.local.DermanKaniImpliedBinomialTreeModel.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.model.volatility.local.DermanKaniImpliedBinomialTreeModel.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.volatility.local;

import org.apache.commons.lang.Validate;
import org.threeten.bp.ZonedDateTime;

import com.opengamma.analytics.financial.model.option.definition.BinomialOptionModelDefinition;
import com.opengamma.analytics.financial.model.option.definition.CoxRossRubinsteinBinomialOptionModelDefinition;
import com.opengamma.analytics.financial.model.option.definition.EuropeanVanillaOptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.OptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.StandardOptionDataBundle;
import com.opengamma.analytics.financial.model.option.pricing.tree.BinomialOptionModel;
import com.opengamma.analytics.financial.model.tree.RecombiningBinomialTree;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Expiry;

/**
 * Implementation of the paper by Derman and Kani, The Volatility Smile and its Implied Tree (1994)
 */
public class DermanKaniImpliedBinomialTreeModel
        implements ImpliedTreeModel<OptionDefinition, StandardOptionDataBundle> {
    private static final BinomialOptionModelDefinition<OptionDefinition, StandardOptionDataBundle> CRR = new CoxRossRubinsteinBinomialOptionModelDefinition();
    private final int _n;

    public DermanKaniImpliedBinomialTreeModel(final int n) {
        Validate.isTrue(n > 0);
        _n = n;
    }

    @Override
    public ImpliedTreeResult getImpliedTrees(final OptionDefinition definition,
            final StandardOptionDataBundle data) {
        Validate.notNull(definition, "definition");
        Validate.notNull(data, "data");

        final int m1 = RecombiningBinomialTree.NODES.evaluate(_n);
        final int m2 = RecombiningBinomialTree.NODES.evaluate(_n - 1);
        final double[][] impliedTree = new double[_n + 1][m1]; //TODO this wastes space

        final double[] transitionProbabilities = new double[m2];
        double[] arrowDebreu = new double[m1];
        final double[][] localVolatilityTree = new double[_n][m2];
        final double dt = definition.getTimeToExpiry(data.getDate()) / _n;
        double t = 0;
        final double spot = data.getSpot();
        impliedTree[0][0] = spot;
        arrowDebreu[0] = 1;
        int previousNodes = 1;
        final ZonedDateTime date = data.getDate();
        for (int i = 1; i < _n + 1; i++) {
            final int nodes = RecombiningBinomialTree.NODES.evaluate(i);
            final BinomialOptionModel<StandardOptionDataBundle> crrModel = new BinomialOptionModel<>(CRR, i);
            t += dt;
            final double df1 = Math.exp(dt * data.getInterestRate(t));
            final double df2 = Math.exp(dt * data.getCostOfCarry());
            final Expiry expiry = new Expiry(DateUtils.getDateOffsetWithYearFraction(date, t));
            final int mid = i / 2;
            if (i % 2 == 0) {
                impliedTree[i][mid] = spot;
                addUpperNodes(data, impliedTree, arrowDebreu, i, crrModel, df1, df2, expiry, mid + 1);
                addLowerNodes(data, impliedTree, arrowDebreu, i, crrModel, df1, df2, expiry, mid - 1);
            } else {
                final double c = crrModel
                        .getTreeGeneratingFunction(new EuropeanVanillaOptionDefinition(spot, expiry, true))
                        .evaluate(data).getNode(0, 0).second;
                final double sigma = getUpperSigma(impliedTree, arrowDebreu, i - 1, df2, mid + 1);
                impliedTree[i][mid + 1] = spot * (df1 * c + arrowDebreu[mid] * spot - sigma)
                        / (arrowDebreu[mid] * impliedTree[i - 1][mid] * df2 - df1 * c + sigma);
                impliedTree[i][mid] = spot * spot / impliedTree[i][mid + 1];
                addUpperNodes(data, impliedTree, arrowDebreu, i, crrModel, df1, df2, expiry, mid + 2);
                addLowerNodes(data, impliedTree, arrowDebreu, i, crrModel, df1, df2, expiry, mid - 1);
            }
            for (int j = 0; j < previousNodes; j++) {
                final double f = impliedTree[i - 1][j] * df2;
                transitionProbabilities[j] = (f - impliedTree[i][j]) / (impliedTree[i][j + 1] - impliedTree[i][j]);
                //TODO emcleod 31-8-10 Need to check that transition probabilities are positive - use adjustment suggested in "The Volatility Smile and its Implied Tree"
                localVolatilityTree[i - 1][j] = Math
                        .sqrt(transitionProbabilities[j] * (1 - transitionProbabilities[j]))
                        * Math.log(impliedTree[i][j + 1] / impliedTree[i][j]); //TODO need 1/sqrt(dt) here
            }
            final double[] temp = new double[m1];
            temp[0] = (1 - transitionProbabilities[0]) * arrowDebreu[0] / df1;
            temp[nodes - 1] = (transitionProbabilities[previousNodes - 1] * arrowDebreu[previousNodes - 1]) / df1;
            for (int j = 1; j < nodes - 1; j++) {
                temp[j] = (transitionProbabilities[j - 1] * arrowDebreu[j - 1]
                        + (1 - transitionProbabilities[j]) * arrowDebreu[j]) / df1;
            }
            arrowDebreu = temp;
            previousNodes = nodes;
        }
        final Double[][] impliedTreeResult = new Double[_n + 1][m1];
        final Double[][] localVolResult = new Double[_n][m2];
        for (int i = 0; i < impliedTree.length; i++) {
            for (int j = 0; j < impliedTree[i].length; j++) {
                impliedTreeResult[i][j] = impliedTree[i][j];
                if (i < _n && j < m2) {
                    localVolResult[i][j] = localVolatilityTree[i][j];
                }
            }
        }
        return new ImpliedTreeResult(new RecombiningBinomialTree<>(impliedTreeResult),
                new RecombiningBinomialTree<>(localVolResult));
    }

    private void addLowerNodes(final StandardOptionDataBundle data, final double[][] impliedTree,
            final double[] arrowDebreu, final int step,
            final BinomialOptionModel<StandardOptionDataBundle> crrModel, final double df1, final double df2,
            final Expiry expiry, final int mid) {
        double sigma = getLowerSigma(impliedTree, arrowDebreu, step - 1, df2, mid);
        for (int i = mid; i >= 0; i--) {
            final double p = crrModel
                    .getTreeGeneratingFunction(
                            new EuropeanVanillaOptionDefinition(impliedTree[step - 1][i], expiry, false))
                    .evaluate(data).getNode(0, 0).second;
            final double forward = impliedTree[step - 1][i] * df2;
            impliedTree[step][i] = (impliedTree[step][i + 1] * (df1 * p - sigma)
                    + arrowDebreu[i] * impliedTree[step - 1][i] * (forward - impliedTree[step][i + 1]))
                    / (df1 * p - sigma + arrowDebreu[i] * (forward - impliedTree[step][i + 1]));
            if (i > 0) {
                sigma -= arrowDebreu[i - 1] * (impliedTree[step - 1][i] - impliedTree[step - 1][i - 1] * df2);
            }
        }
    }

    private void addUpperNodes(final StandardOptionDataBundle data, final double[][] impliedTree,
            final double[] arrowDebreu, final int step,
            final BinomialOptionModel<StandardOptionDataBundle> crrModel, final double df1, final double df2,
            final Expiry expiry, final int mid) {
        double sigma = getUpperSigma(impliedTree, arrowDebreu, step - 1, df2, mid);
        for (int i = mid; i < RecombiningBinomialTree.NODES.evaluate(step); i++) {
            final double c = crrModel
                    .getTreeGeneratingFunction(
                            new EuropeanVanillaOptionDefinition(impliedTree[step - 1][i - 1], expiry, true))
                    .evaluate(data).getNode(0, 0).second;
            final double forward = impliedTree[step - 1][i - 1] * df2;
            impliedTree[step][i] = (impliedTree[step][i - 1] * (df1 * c - sigma)
                    - arrowDebreu[i - 1] * impliedTree[step - 1][i - 1] * (forward - impliedTree[step][i - 1]))
                    / (df1 * c - sigma - arrowDebreu[i - 1] * (forward - impliedTree[step][i - 1]));
            sigma -= arrowDebreu[i] * (impliedTree[step - 1][i] * df2 - impliedTree[step - 1][i - 1]);
        }
    }

    private double getLowerSigma(final double[][] impliedTree, final double[] arrowDebreu, final int previousStep,
            final double df2, final int start) {
        double sigma = 0;
        for (int i = start - 1; i >= 0; i--) {
            sigma += arrowDebreu[i] * (impliedTree[previousStep][start] - impliedTree[previousStep][i] * df2);
        }
        return sigma;
    }

    private double getUpperSigma(final double[][] impliedTree, final double[] arrowDebreu, final int previousStep,
            final double df2, final int start) {
        double sigma = 0;
        for (int i = start; i < RecombiningBinomialTree.NODES.evaluate(previousStep + 1); i++) {
            sigma += arrowDebreu[i] * (impliedTree[previousStep][i] * df2 - impliedTree[previousStep][start - 1]);
        }
        return sigma;
    }

}