AnalyticOptionValuation.java
package net.finmath.equities.pricer;
import java.util.HashMap;
import org.apache.commons.lang3.NotImplementedException;
import net.finmath.equities.marketdata.YieldCurve;
import net.finmath.equities.models.Black76Model;
import net.finmath.equities.models.EquityForwardStructure;
import net.finmath.equities.models.VolatilitySurface;
import net.finmath.equities.pricer.EquityValuationRequest.CalculationRequestType;
import net.finmath.equities.products.Option;
import net.finmath.time.daycount.DayCountConvention;
/**
* This class implements analytic pricing formulas for European options under a
* Black-Scholes process with Buehler dividends.
*
* @author Andreas Grotz
*/
public class AnalyticOptionValuation implements OptionValuation {
private final DayCountConvention dcc;
public AnalyticOptionValuation(DayCountConvention dcc) {
this.dcc = dcc;
}
@Override
public EquityValuationResult calculate(
EquityValuationRequest request,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
VolatilitySurface volaSurface) {
final HashMap<CalculationRequestType, Double> results = new HashMap<CalculationRequestType, Double>();
for(final CalculationRequestType calcType : request.getCalcsRequested()) {
results.put(
calcType,
calculate(request.getOption(), forwardStructure, discountCurve, volaSurface, calcType));
}
return new EquityValuationResult(request, results);
}
public double calculate(
Option option,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
VolatilitySurface volaSurface,
CalculationRequestType calcType) {
assert !option.isAmericanOption() : "Analytic pricer cannot handle American options.";
final java.time.LocalDate valDate = forwardStructure.getValuationDate();
final java.time.LocalDate expiryDate = option.getExpiryDate();
final double ttm = dcc.getDaycountFraction(forwardStructure.getValuationDate(), expiryDate);
final double forward = forwardStructure.getForward(expiryDate);
final double discountFactor = discountCurve.getDiscountFactor(expiryDate);
final double discountRate = discountCurve.getRate(expiryDate);
final double adjustedForward = forwardStructure.getDividendAdjustedStrike(forward, expiryDate);
final double adjustedStrike = forwardStructure.getDividendAdjustedStrike(option.getStrike(), expiryDate);
final double volatility = volaSurface.getVolatility(option.getStrike(), option.getExpiryDate(), forwardStructure);
switch(calcType) {
case Price:
return Black76Model.optionPrice(
1.0,
adjustedStrike / adjustedForward,
ttm,
volatility,
option.isCallOption(),
discountFactor * adjustedForward);
case EqDelta:
final double dFdS = forwardStructure.getGrowthDiscountFactor(valDate, expiryDate);
return dFdS * Black76Model.optionDelta(
1.0,
adjustedStrike / adjustedForward,
ttm,
volatility,
option.isCallOption(),
discountFactor);
case EqGamma:
final double dFdS2 = Math.pow(forwardStructure.getGrowthDiscountFactor(valDate, expiryDate), 2);
return dFdS2 * Black76Model.optionGamma(
1.0,
adjustedStrike / adjustedForward,
ttm,
volatility,
option.isCallOption(),
discountFactor / adjustedForward);
case EqVega:
return Black76Model.optionVega(
1.0,
adjustedStrike / adjustedForward,
ttm,
volatility,
option.isCallOption(),
discountFactor * adjustedForward);
case Theta:
return Black76Model.optionTheta(
1.0,
adjustedStrike / adjustedForward,
ttm,
volatility,
option.isCallOption(),
discountFactor * adjustedForward,
discountRate);
default:
throw new NotImplementedException("Calculation for " + calcType + " not implemented yet.");
}
}
public double getPrice(
Option option,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
VolatilitySurface volaSurface) {
return calculate(option, forwardStructure, discountCurve, volaSurface, CalculationRequestType.Price);
}
public double getDelta(
Option option,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
VolatilitySurface volaSurface) {
return calculate(option, forwardStructure, discountCurve, volaSurface, CalculationRequestType.EqDelta);
}
public double getGamma(
Option option,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
VolatilitySurface volaSurface) {
return calculate(option, forwardStructure, discountCurve, volaSurface, CalculationRequestType.EqGamma);
}
public double getVega(
Option option,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
VolatilitySurface volaSurface) {
return calculate(option, forwardStructure, discountCurve, volaSurface, CalculationRequestType.EqVega);
}
public double getTheta(
Option option,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
VolatilitySurface volaSurface) {
return calculate(option, forwardStructure, discountCurve, volaSurface, CalculationRequestType.Theta);
}
public double getImpliedVolatility(
Option option,
EquityForwardStructure forwardStructure,
YieldCurve discountCurve,
double price) {
assert !option.isAmericanOption() : "Analytic pricer cannot handle American options.";
final java.time.LocalDate expiryDate = option.getExpiryDate();
final double ttm = dcc.getDaycountFraction(forwardStructure.getValuationDate(), expiryDate);
final double forward = forwardStructure.getForward(expiryDate);
final double discount = discountCurve.getDiscountFactor(expiryDate);
final double adjustedForward = forwardStructure.getDividendAdjustedStrike(forward, expiryDate);
final double adjustedStrike = forwardStructure.getDividendAdjustedStrike(option.getStrike(), expiryDate);
final double undiscountedPrice = price / discount / adjustedForward;
return Black76Model.optionImpliedVolatility(
1.0,
adjustedStrike / adjustedForward,
ttm,
undiscountedPrice,
option.isCallOption());
}
}