/**
* 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.analytic;
import java.util.Collections;
import java.util.Set;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.financial.greeks.Greek;
import com.opengamma.analytics.financial.greeks.GreekResultCollection;
import com.opengamma.analytics.financial.model.option.definition.AmericanVanillaOptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.OptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.StandardOptionDataBundle;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.rootfinding.BisectionSingleRootFinder;
import com.opengamma.analytics.math.rootfinding.RealSingleRootFinder;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;
/**
*
*/
public class JuZhongModel extends AnalyticOptionModel<AmericanVanillaOptionDefinition, StandardOptionDataBundle> {
private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1);
private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BSM = new BlackScholesMertonModel();
private static final Set<Greek> PRICE = Collections.singleton(Greek.FAIR_PRICE);
private static final RealSingleRootFinder FINDER = new BisectionSingleRootFinder();
@Override
public Function1D<StandardOptionDataBundle, Double> getPricingFunction(final AmericanVanillaOptionDefinition definition) {
Validate.notNull(definition);
final double phi = definition.isCall() ? 1 : -1;
final Function1D<StandardOptionDataBundle, Double> pricingFunction = new Function1D<StandardOptionDataBundle, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final StandardOptionDataBundle data) {
Validate.notNull(data);
final GreekResultCollection bsmResult = BSM.getGreeks(definition, data, PRICE);
final double bsmPrice = bsmResult.get(Greek.FAIR_PRICE);
final double s = data.getSpot();
final double k = definition.getStrike();
final double t = definition.getTimeToExpiry(data.getDate());
final double r = data.getInterestRate(t);
final double b = data.getCostOfCarry();
final double sigma = data.getVolatility(t, k);
final double sigmaSq = sigma * sigma;
final double h = getH(r, t);
final double alpha = getAlpha(r, sigmaSq);
final double beta = getBeta(r, b, sigmaSq);
final double lambda = getLambda(phi, alpha, beta, h);
final double lambdaDash = getLambdaDash(phi, alpha, beta, h);
final Function1D<Double, Double> function = getFunction(phi, Math.exp(-b * t), k, t, sigmaSq, b, lambda, definition, data);
final double sEstimate = FINDER.getRoot(function, 0., s * 2);
if (phi * (sEstimate - s) <= 0) {
return phi * (s - k);
}
final double estimatePrice = BSM.getGreeks(definition, data.withSpot(sEstimate), PRICE).get(Greek.FAIR_PRICE);
final double hA = phi * (sEstimate - k) - estimatePrice;
final double derivative = getDerivative(k, r, b, t, sigma, phi, sEstimate);
final double c = getC(h, alpha, lambdaDash, lambda, beta);
final double d = getD(h, alpha, derivative, hA, lambdaDash, lambda, beta);
final double ratio = Math.log(s / sEstimate);
final double chi = c * Math.pow(ratio, 2) + d * ratio;
return bsmPrice + hA * Math.pow(s / sEstimate, lambda) / (1 - chi);
}
};
return pricingFunction;
}
protected double getH(final double r, final double t) {
return 1 - Math.exp(-r * t);
}
protected double getAlpha(final double r, final double sigmaSq) {
return 2 * r / sigmaSq;
}
protected double getBeta(final double r, final double b, final double sigmaSq) {
return 2 * (r - b) / sigmaSq;
}
protected double getLambda(final double phi, final double alpha, final double beta, final double h) {
final double beta1 = beta - 1;
return (-beta + phi * Math.sqrt(beta1 * beta1 + 4 * alpha / h)) / 2;
}
protected double getLambdaDash(final double phi, final double alpha, final double beta, final double h) {
return phi * alpha / (h * h * Math.sqrt((beta - 1) * (beta - 1) + 4 * alpha / h));
}
protected double getC(final double h, final double alpha, final double lambdaDash, final double lambda, final double beta) {
return (1 - h) * alpha * lambdaDash / (2 * (2 * lambda + beta - 1));
}
protected double getD(final double h, final double alpha, final double derivative, final double hA, final double lambdaDash, final double lambda, final double beta) {
final double denom = 2 * lambda + beta - 1;
return (1 - h) * alpha * (derivative / hA + 1 / h + lambdaDash / denom) / denom;
}
protected double getDerivative(final double k, final double r, final double b, final double t, final double sigma, final double phi, final double sEstimate) {
final double df = Math.exp(t * (r - b));
final double d1 = getD1(sEstimate, k, t, sigma, b);
final double d2 = getD2(d1, sigma, t);
return sEstimate * NORMAL.getPDF(d1) * sigma * df / (2 * r * Math.sqrt(t)) - phi * b * sEstimate * NORMAL.getCDF(phi * d1) * df / r + phi * k * NORMAL.getCDF(phi * d2);
}
protected Function1D<Double, Double> getFunction(final double phi, final double df, final double k, final double t, final double sigma, final double b, final double lambda,
final OptionDefinition definition, final StandardOptionDataBundle data) {
return new Function1D<Double, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double x) {
final GreekResultCollection bsmPrice = BSM.getGreeks(definition, data.withSpot(x), PRICE);
final double price = bsmPrice.get(Greek.FAIR_PRICE);
return phi * (df * NORMAL.getCDF(phi * getD1(x, k, t, sigma, b)) + lambda * (phi * (x - k) - price) / x - 1);
}
};
}
}