/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.interestrate.curve;
import static com.opengamma.analytics.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils.getLowerBoundIndex;
import org.apache.commons.lang.ObjectUtils;
import com.opengamma.analytics.financial.equity.variance.pricing.AffineDividends;
import com.opengamma.analytics.math.curve.ConstantDoublesCurve;
import com.opengamma.analytics.math.curve.Curve;
import com.opengamma.analytics.math.curve.FunctionalDoublesCurve;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.util.serialization.InvokedSerializedForm;
import com.opengamma.util.ArgumentChecker;
/**
* ForwardCurve for Equity assets that are modelled to pay known discrete dividends
* with an affine form: d(i) = alpha[i] + beta[i]*share_price(tau[i])
*/
public class ForwardCurveAffineDividends extends ForwardCurve {
private final YieldAndDiscountCurve _riskFreeCurve;
private final AffineDividends _dividends;
public ForwardCurveAffineDividends(final double spot, final YieldAndDiscountCurve riskFreeCurve, final AffineDividends dividends) {
super(getForwardCurve(spot, riskFreeCurve, dividends));
_riskFreeCurve = riskFreeCurve;
_dividends = dividends;
}
/**
* @param spot The spot, greater than zero
* @param riskFreeCurve The discount curve, not null
* @param dividends The dividends, not null
* @return FunctionalDoublesCurve with discrete dividends of an affine form: d(i) = alpha[i] + beta[i]*share_price(tau[i])
*/
protected static Curve<Double, Double> getForwardCurve(final double spot, final YieldAndDiscountCurve riskFreeCurve, final AffineDividends dividends) {
ArgumentChecker.isTrue(spot > 0, "Negative spot. S_0 = {}", spot);
ArgumentChecker.notNull(riskFreeCurve, "null risk free curve");
ArgumentChecker.notNull(dividends, "null dividends");
if (dividends.getNumberOfDividends() == 0) {
return getForwardCurve(spot, riskFreeCurve, YieldCurve.from(ConstantDoublesCurve.from(0.0)));
}
final Function1D<Double, Double> f = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double t) {
final int n = dividends.getNumberOfDividends();
final double[] growthFactor = new double[n];
final double[] accumProd = new double[n];
final double[] accumSum = new double[n];
double prod = 1.0;
double sum = 0.0;
for (int i = 0; i < n; i++) {
prod *= (1 - dividends.getBeta(i));
accumProd[i] = prod;
growthFactor[i] = prod / riskFreeCurve.getDiscountFactor(dividends.getTau(i));
sum += dividends.getAlpha(i) / growthFactor[i];
accumSum[i] = sum;
}
if (t < dividends.getTau(0)) {
return spot / riskFreeCurve.getDiscountFactor(t);
}
final int index = getLowerBoundIndex(dividends.getTau(), t);
final double total = accumSum[index];
return accumProd[index] / riskFreeCurve.getDiscountFactor(t) * (spot - total);
}
};
return new FunctionalDoublesCurve(f) {
public Object writeReplace() {
return new InvokedSerializedForm(ForwardCurveAffineDividends.class, "getForwardCurve", spot, riskFreeCurve, dividends);
}
};
}
public YieldAndDiscountCurve getRiskFreeCurve() {
return _riskFreeCurve;
}
public AffineDividends getDividends() {
return _dividends;
}
/**
* Shift the forward curve by a fractional amount, shift, such that the new curve F'(T) = (1 + shift) * F(T), has
* an unchanged drift.
* @param shift The fractional shift amount, i.e. 0.1 will produce a curve 10% larger than the original
* @return The shifted curve
*/
@Override
public ForwardCurveAffineDividends withFractionalShift(final double shift) {
ArgumentChecker.isTrue(shift > -1, "shift must be > -1");
return new ForwardCurveAffineDividends((1 + shift) * getSpot(), getRiskFreeCurve(), getDividends());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getDriftCurve().hashCode();
result = prime * result + getForwardCurve().hashCode();
long temp;
temp = Double.doubleToLongBits(getSpot());
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + getRiskFreeCurve().hashCode();
result = prime * result + getDividends().hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ForwardCurveAffineDividends other = (ForwardCurveAffineDividends) obj;
if (!ObjectUtils.equals(getRiskFreeCurve(), other.getRiskFreeCurve())) {
return false;
}
if (!ObjectUtils.equals(getDividends(), other.getDividends())) {
return false;
}
if (Double.doubleToLongBits(getSpot()) != Double.doubleToLongBits(other.getSpot())) {
return false;
}
return true;
}
}