/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.equity.variance.pricing;
import static com.opengamma.analytics.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils.getLowerBoundIndex;
import org.apache.commons.lang.ObjectUtils;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.util.ArgumentChecker;
/**
* Contains forward, interest rate and dividend information.
*/
public class EquityDividendsCurvesBundle {
/** The forward curve function */
private final Function1D<Double, Double> _f;
/** The growth factor curve function */
private final Function1D<Double, Double> _r;
/** The growth factor discounted cash dividends curve function */
private final Function1D<Double, Double> _d;
/**
* @param spot The spot, greater than zero
* @param discountCurve The discount curve, not null
* @param dividends The dividends, not null
*/
public EquityDividendsCurvesBundle(final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends) {
ArgumentChecker.isTrue(spot > 0, "Negative spot. S_0 = {}", spot);
ArgumentChecker.notNull(discountCurve, "null discount curve");
ArgumentChecker.notNull(dividends, "null dividends");
_r = getGrowthFactorCurve(discountCurve, dividends);
_d = getDiscountedCashDividendsCurve(_r, dividends);
_f = getForwardCurve(spot, _r, dividends);
}
/**
* Gets the forward curve.
* @return the forward curve
*/
public Function1D<Double, Double> getF() {
return _f;
}
/**
* Gets the growth factor curve
* @return the growth factor curve
*/
public Function1D<Double, Double> getR() {
return _r;
}
/**
* Gets the growth factor discounted cash dividends curve
* @return the growth factor discounted cash dividends curve
*/
public Function1D<Double, Double> getD() {
return _d;
}
/**
* Gets the forward value
* @param t time
* @return the forward value
*/
public double getF(final double t) {
return _f.evaluate(t);
}
/**
* Gets the Growth Factor value
* @param t time
* @return the growth factor value
*/
public double getR(final double t) {
return _r.evaluate(t);
}
/**
* Gets the growth factor discounted cash dividends value
* @param t time
* @return the growth factor discounted cash dividends value
*/
public double getD(final double t) {
return _d.evaluate(t);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + _d.hashCode();
result = prime * result + _f.hashCode();
result = prime * result + _r.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
EquityDividendsCurvesBundle other = (EquityDividendsCurvesBundle) obj;
if (!ObjectUtils.equals(_d, other._d)) {
return false;
}
if (!ObjectUtils.equals(_f, other._f)) {
return false;
}
if (!ObjectUtils.equals(_r, other._r)) {
return false;
}
return true;
}
private static Function1D<Double, Double> getForwardCurve(final double spot, final Function1D<Double, Double> growthFactorCurve, final AffineDividends dividends) {
if (dividends.getNumberOfDividends() == 0) {
return new Function1D<Double, Double>() {
@Override
public Double evaluate(Double t) {
return spot * growthFactorCurve.evaluate(t);
}
};
}
final int n = dividends.getNumberOfDividends();
final double[] accum = new double[n];
double sum = dividends.getAlpha(0) / growthFactorCurve.evaluate(dividends.getTau(0));
accum[0] = sum;
for (int i = 1; i < n; i++) {
sum += dividends.getAlpha(i) / growthFactorCurve.evaluate(dividends.getTau(i));
accum[i] = sum;
}
return new Function1D<Double, Double>() {
@Override
public Double evaluate(Double t) {
if (t < dividends.getTau(0)) {
return spot * growthFactorCurve.evaluate(t);
}
int index = getLowerBoundIndex(dividends.getTau(), t);
double total = accum[index];
return growthFactorCurve.evaluate(t) * (spot - total);
}
};
}
private static Function1D<Double, Double> getDiscountedCashDividendsCurve(final Function1D<Double, Double> growthFactorCurve, final AffineDividends dividends) {
if (dividends.getNumberOfDividends() == 0) {
return new Function1D<Double, Double>() {
@Override
public Double evaluate(Double t) {
return 0.0;
}
};
}
final int n = dividends.getNumberOfDividends();
final double[] accum = new double[n];
double sum = dividends.getAlpha(n - 1) / growthFactorCurve.evaluate(dividends.getTau(n - 1));
accum[n - 1] = sum;
for (int i = n - 2; i >= 0; i--) {
sum += dividends.getAlpha(i) / growthFactorCurve.evaluate(dividends.getTau(i));
accum[i] = sum;
}
return new Function1D<Double, Double>() {
@Override
public Double evaluate(Double t) {
if (t >= dividends.getTau(n - 1)) {
return 0.0;
}
if (t < dividends.getTau(0)) {
return growthFactorCurve.evaluate(t) * accum[0];
}
int index = getLowerBoundIndex(dividends.getTau(), t) + 1;
double total = accum[index];
return growthFactorCurve.evaluate(t) * total;
}
};
}
private static Function1D<Double, Double> getGrowthFactorCurve(final YieldAndDiscountCurve discCurve, final AffineDividends dividends) {
if (dividends.getNumberOfDividends() == 0) {
return new Function1D<Double, Double>() {
@Override
public Double evaluate(Double t) {
return 1.0 / discCurve.getDiscountFactor(t);
}
};
}
final int n = dividends.getNumberOfDividends();
final double[] accum = new double[n];
double prod = (1 - dividends.getBeta(0));
accum[0] = prod;
for (int i = 1; i < n; i++) {
prod *= (1 - dividends.getBeta(i));
accum[i] = prod;
}
return new Function1D<Double, Double>() {
@Override
public Double evaluate(Double t) {
if (t < dividends.getTau(0)) {
return 1.0 / discCurve.getDiscountFactor(t);
}
int index = getLowerBoundIndex(dividends.getTau(), t);
double total = accum[index];
return total / discCurve.getDiscountFactor(t);
}
};
}
}