/**
* 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.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.greeks.GreekVisitor;
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.FiniteDifferenceGreekVisitor;
import com.opengamma.analytics.financial.model.option.pricing.OptionModel;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;
import com.opengamma.util.CompareUtils;
/**
* @param <T> The type of the option definition
* @param <U> The type of the option data bundle
* Base class for analytic option models.
*/
public abstract class AnalyticOptionModel<T extends OptionDefinition, U extends StandardOptionDataBundle> implements OptionModel<T, U> {
/**
* Returns a pricing function.
* @param definition The option definition, not null
* @return The pricing function
*/
public abstract Function1D<U, Double> getPricingFunction(T definition);
/**
* Returns a visitor that calculates greeks. By default, the calculation method is finite difference. If a different
* method is possible (e.g. analytic formulae) then this method should be overridden in the implementing class.
* @param pricingFunction The pricing function, not null
* @param data The data, not null
* @param definition The option definition, not null
* @return A visitor that calculates greeks
*/
public GreekVisitor<Double> getGreekVisitor(final Function1D<U, Double> pricingFunction, final U data, final T definition) {
Validate.notNull(pricingFunction);
Validate.notNull(data);
Validate.notNull(definition);
return new AnalyticOptionModelFiniteDifferenceGreekVisitor<>(pricingFunction, data, definition);
}
/**
* {@inheritDoc}
*/
@Override
public GreekResultCollection getGreeks(final T definition, final U data, final Set<Greek> requiredGreeks) {
Validate.notNull(definition);
Validate.notNull(data);
Validate.notNull(requiredGreeks);
final Function1D<U, Double> pricingFunction = getPricingFunction(definition);
final GreekResultCollection results = new GreekResultCollection();
final GreekVisitor<Double> visitor = getGreekVisitor(pricingFunction, data, definition);
for (final Greek greek : requiredGreeks) {
final Double result = greek.accept(visitor);
results.put(greek, result);
}
return results;
}
protected double getD1(final double s, final double k, final double t, final double sigma, final double b) {
final double numerator = (Math.log(s / k) + t * (b + sigma * sigma / 2));
if (CompareUtils.closeEquals(numerator, 0, 1e-16)) {
return 0;
}
return numerator / (sigma * Math.sqrt(t));
}
protected double getD2(final double d1, final double sigma, final double t) {
return d1 - sigma * Math.sqrt(t);
}
protected double getDF(final double r, final double b, final double t) {
return Math.exp(t * (b - r));
}
/**
* Extends the finite difference greek visitor to allow calculation of dZetaDVol
* @param <S> The type of the option data bundle
* @param <R> The type of the option definition
*/
protected class AnalyticOptionModelFiniteDifferenceGreekVisitor<S extends StandardOptionDataBundle, R extends OptionDefinition> extends FiniteDifferenceGreekVisitor<S, R> {
private static final double EPS = 1e-3;
private final S _data;
private final R _definition;
private final ProbabilityDistribution<Double> _normal = new NormalDistribution(0, 1);
public AnalyticOptionModelFiniteDifferenceGreekVisitor(final Function1D<S, Double> pricingFunction, final S data, final R definition) {
super(pricingFunction, data, definition);
_data = data;
_definition = definition;
}
@Override
public Double visitDZetaDVol() {
final double s = _data.getSpot();
final double k = _definition.getStrike();
final double t = _definition.getTimeToExpiry(_data.getDate());
final double b = _data.getCostOfCarry();
final double sigma = _data.getVolatility(t, k);
final int sign = _definition.isCall() ? 1 : -1;
final double nUp = _normal.getCDF(sign * getD2(getD1(s, k, t, sigma + EPS, b), sigma + EPS, t));
final double nDown = _normal.getCDF(sign * getD2(getD1(s, k, t, sigma - EPS, b), sigma - EPS, t));
return (nUp - nDown) / (2 * EPS);
}
}
}