/** * Copyright (C) 2013 - 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 java.util.Arrays; import com.google.common.primitives.Doubles; import com.opengamma.util.ArgumentChecker; /** * Provide functions of discrete dividend, needed for tree option pricing */ public abstract class DividendFunctionProvider { private double[] _dividendTimes; private double[] _dividends; private int _nDividends; /** * Constructor. Dividend data must be sorted in chronological order * @param dividendTimes The dividend times * @param dividends The dividend payment amount/ratio */ public DividendFunctionProvider(final double[] dividendTimes, final double[] dividends) { ArgumentChecker.notNull(dividendTimes, "dividendTimes"); ArgumentChecker.notNull(dividends, "dividends"); final int nDiv = dividendTimes.length; ArgumentChecker.isTrue(nDiv == dividends.length, "Wrong data length"); for (int i = 0; i < nDiv; ++i) { ArgumentChecker.isTrue(dividendTimes[i] > 0., "dividendTimes should be positive"); ArgumentChecker.isTrue(Doubles.isFinite(dividendTimes[i]), "dividendTimes should be finite"); ArgumentChecker.isTrue(dividends[i] > 0., "dividends should be positive"); ArgumentChecker.isTrue(Doubles.isFinite(dividends[i]), "dividends should be finite"); } for (int i = 1; i < nDiv; ++i) { ArgumentChecker.isTrue(dividendTimes[i] - dividendTimes[i - 1] > 0., "dividendTimes should be in ascending order"); } _dividendTimes = Arrays.copyOf(dividendTimes, nDiv); _dividends = Arrays.copyOf(dividends, nDiv); _nDividends = nDiv; } /** * Compute the asset price modified due to dividend payments * @param spot The spot price of asset * @param interestRate The interest rate * @return The modified spot price */ public abstract double spotModifier(final double spot, final double interestRate); /** * Compute correction to asset price due to dividends up to the k-th payment * @param assetPrice The asset price just before the k-th payment * @param interestRate The interest rate * @param offset Time in the layer just before the k-th payment * @param k * @return The correction */ public abstract double dividendCorrections(final double assetPrice, final double interestRate, final double offset, final int k); /** * Asset prices in the 1st layer, i.e., S_{10} and S_{11} * @param spot The spot * @param interestRate The interest rate * @param divSteps The position of layers where dividends are paid * @param upFactor Up factor * @param downFactor Down factor * @param sumDiscountDiv Sum of discounted (cash) dividends * @return { S_{10}, S_{11} } */ public abstract double[] getAssetPricesForDelta(final double spot, final double interestRate, final int[] divSteps, final double upFactor, final double downFactor, final double sumDiscountDiv); /** * Asset prices in the second layer, i.e., S_{20}, S_{21} and S_{22} * @param spot The spot * @param interestRate The interest rate * @param divSteps The positions of layers where dividends are paid * @param upFactor Up factor * @param downFactor Down factor * @param sumDiscountDiv Sum of discounted (cash) dividends * @return { S_{20}, S_{21}, S_{22} } */ public abstract double[] getAssetPricesForGamma(final double spot, final double interestRate, final int[] divSteps, final double upFactor, final double downFactor, final double sumDiscountDiv); /** * Asset prices in the 1st layer, i.e., S_{10} and S_{11} * @param spot The spot * @param interestRate The interest rate * @param divSteps The position of layers where dividends are paid * @param upFactor Up factor * @param middleFactor Middle factor * @param downFactor Down factor * @param sumDiscountDiv Sum of discounted (cash) dividends * @return { S_{10}, S_{11}, S_{12} } */ public abstract double[] getAssetPricesForDelta(final double spot, final double interestRate, final int[] divSteps, final double upFactor, final double middleFactor, final double downFactor, final double sumDiscountDiv); /** * Asset prices in the second layer, i.e., S_{20}, S_{21} and S_{22} * @param spot The spot * @param interestRate The interest rate * @param divSteps The positions of layers where dividends are paid * @param upFactor Up factor * @param middleFactor Middle factor * @param downFactor Down factor * @param sumDiscountDiv Sum of discounted (cash) dividends * @return { S_{20}, S_{21}, S_{22}, S_{23}, S_{24} } */ public abstract double[] getAssetPricesForGamma(final double spot, final double interestRate, final int[] divSteps, final double upFactor, final double middleFactor, final double downFactor, final double sumDiscountDiv); /** * @param dt Time step * @return The positions of layers where dividends are paid */ public int[] getDividendSteps(final double dt) { final int nDivs = _dividendTimes.length; final int[] divSteps = new int[nDivs]; for (int i = 0; i < nDivs; ++i) { divSteps[i] = (int) (_dividendTimes[i] / dt); } return divSteps; } /** * Compare time step width with payment time width * @param dt Time step width * @return True if (time step width) < (payment time width) for all dividends */ public boolean checkTimeSteps(final double dt) { final int nDivM = _nDividends - 1; for (int i = 0; i < nDivM; ++i) { if (_dividendTimes[i + 1] - _dividendTimes[i] < dt) { return false; } } return true; } /** * @param timeToExpiry Time to expiry * @return True if all of the dividend times are before expiry, false otherwise */ public boolean checkDividendBeforeExpiry(final double timeToExpiry) { final int nDiv = _nDividends; for (int i = 0; i < nDiv; ++i) { if (_dividendTimes[i] > timeToExpiry) { return false; } } return true; } /** * Access dividend times * @return _dividendTimes */ public double[] getDividendTimes() { return _dividendTimes; } /** * Access dividend amount/ratio * @return _dividends */ public double[] getDividends() { return _dividends; } /** * Access number of dividend payments * @return _nDividends */ public int getNumberOfDividends() { return _nDividends; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(_dividendTimes); result = prime * result + Arrays.hashCode(_dividends); result = prime * result + _nDividends; return result; } @Override public boolean equals(Object obj) { /* * This case is always successful because this equals() is necessarily called by a subclass */ DividendFunctionProvider other = (DividendFunctionProvider) obj; if (!Arrays.equals(_dividendTimes, other._dividendTimes)) { return false; } if (!Arrays.equals(_dividends, other._dividends)) { return false; } return true; } }