/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.rootfinding.NewtonRaphsonSingleRootFinder;
import com.opengamma.util.CompareUtils;
/**
* Black pricing in the forward measure. All prices, input/output, are *forward* prices, i.e. price(t,T) / Zero(t,T).
* Similar to http://en.wikipedia.org/wiki/Black_model with fwdMtm = c / exp(-rT).
* This permits us to disregard discounting, which is sufficient for purpose of implied volatility.
*/
public class BlackFormula {
private double _forward;
private double _strike;
private double _expiry;
private Double _lognormalVol;
private Double _fwdMtm;
private boolean _isCall;
/**
* @param forward Forward value of the underlying. The fair value agreed today to exchange for the underlying at expiry
* @param strike Strike of the option in question. If equal to the forward, it is considered at-the-money
* @param expiry Expiry date, as a double representing the number of years until the option expires
* @param lognormalVol Average lognormal volatility of the underlying that reproduces the price. May be null.
* @param fwdPrice Forward, i.e. undiscounted, price of the option. May be null.
* @param isCall True if the option is a Call; false if it is a Put.
* @deprecated This is stateful.
*/
@Deprecated
public BlackFormula(final double forward, final double strike, final double expiry, final Double lognormalVol, final Double fwdPrice, final boolean isCall) {
_forward = forward;
_strike = strike;
_expiry = expiry;
_lognormalVol = lognormalVol;
_fwdMtm = fwdPrice;
_isCall = isCall;
}
/**
* Computes the forward price from forward, strike and variance.
* This is NOT a getter of the data member, _fwdMtm. For that, use getFwdMtm().
* Nor does this set any data member.
* @return Black formula price of the option
*/
public final double computePrice() {
Validate.notNull(_lognormalVol, "Black Volatility parameter, _vol, has not been set.");
return BlackFormulaRepository.price(_forward, _strike, _expiry, _lognormalVol, _isCall);
}
/**
* Computes the derivative of the *forward price* with respect to the forward.
* If the current value of a call with expiry, T is C(0,T), the forward price is this normalized by the terminal bond, Z(0,T) = exp(-rT).
* Hence *forward price* c(0,T) = C(0,T) / Z(0,T). And ForwardDelta = dc/dF == d(C/Z)/dF
* This is NOT a getter of the data member, _fwdMtm. For that, use getFwdMtm().
* Nor does this set any data member.
* @return d/dF [C(0,T) / Z(0,T)]
*/
public final double computeForwardDelta() {
Validate.notNull(_lognormalVol, "Black Volatility parameter, _vol, has not been set.");
return BlackFormulaRepository.delta(_forward, _strike, _expiry, _lognormalVol, _isCall);
}
/**
* Via the BlackFormula, this converts a Delta-parameterised strike into the actual strike value.
* fwdDelta, defined here, is always positive, in [0,1]. At 0.5, a straddle has zero delta, occurring at K == F*exp(0.5 sig^2 T). So deltas < 0.5 are out-of-the-money.
* Hence a 0.25 put and a 0.25 call will have a strike less and greater than the forward, respectively.
* @param fwdDelta the first order derivative of the price with respect to the forward
* @param forCall Choose whether you would like to see the strike of the call or put for the fwdDelta provided
* @return The true strike value
*/
public final Double computeStrikeImpliedByForwardDelta(final double fwdDelta, final boolean forCall) {
Validate.notNull(_lognormalVol, "Black Volatility parameter, _vol, has not been set.");
return BlackFormulaRepository.strikeForDelta(_forward, fwdDelta, _expiry, _lognormalVol, forCall);
}
public final double computeStrikeSensitivity() {
Validate.notNull(_lognormalVol, "Black Volatility parameter, _vol, has not been set.");
return BlackFormulaRepository.dualDelta(_forward, _strike, _expiry, _lognormalVol, _isCall);
}
/**
* Computes the implied lognormal volatility from forward mark-to-market, forward underlying and strike.
* This is NOT a getter of the data member, _lognormalVol. For that, use getLognormalVol().
* Nor does this set any data member.
* @return Black formula price of the option
*/
public final double computeImpliedVolatility() {
Validate.notNull(_fwdMtm, "price is not set. Cannot compute implied volatility. call setMtm first");
return BlackFormulaRepository.impliedVolatility(_fwdMtm.doubleValue(), _forward, _strike, _expiry, _isCall);
}
// The following function was used to test the other.
// Will likely be useful when dealing with manipulating strike and vol as they depend on each other in delta parameterisation
public final Double computeStrikeImpliedByDeltaViaRootFinding(final double fwdDelta, final boolean forCall) {
try {
Validate.isTrue(fwdDelta >= 0.0 && fwdDelta <= 1.0, "Delta must be between 0.0 and 1.0");
final BlackFormula black = new BlackFormula(_forward, _strike, _expiry, _lognormalVol, _fwdMtm, forCall);
final Function1D<Double, Double> difference = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double strike) {
black.setStrike(strike);
final double delta = black.computeForwardDelta();
return delta - fwdDelta * (forCall ? 1.0 : -1.0); // TODO Case : confirm this is sufficient for calls and puts
}
};
final NewtonRaphsonSingleRootFinder rootFinder = new NewtonRaphsonSingleRootFinder();
if ((forCall && fwdDelta >= 0.5) || (!forCall && fwdDelta <= 0.5)) {
// Strike is bounded below by 0 and above by the atmDelta
final double atmDelta = _forward * Math.exp(0.5 * _lognormalVol * _lognormalVol * _expiry);
return rootFinder.getRoot(difference, 0.0, atmDelta);
} // Give it a guess, and estimate finite-difference derivative
return rootFinder.getRoot(difference, _strike);
} catch (final com.opengamma.analytics.math.MathException e) {
System.err.println(e);
System.err.println("Failed to compute ImpliedVolatility");
return null;
}
}
/**
* Gets the forward.
* @return the forward
*/
public final double getForward() {
return _forward;
}
/**
* Sets the forward.
* @param forward the forward
*/
public final void setForward(final double forward) {
_forward = forward;
}
/**
* Gets the strike.
* @return the strike
*/
public final double getStrike() {
return _strike;
}
/**
* Sets the strike.
* @param strike the strike
*/
public final void setStrike(final double strike) {
_strike = strike;
}
/**
* Gets the expiry.
* @return the expiry
*/
public final double getExpiry() {
return _expiry;
}
/**
* Sets the expiry.
* @param expiry the expiry
*/
public final void setExpiry(final double expiry) {
_expiry = expiry;
}
/**
* Gets the vol.
* @return the vol
*/
public final Double getLognormalVol() {
return _lognormalVol;
}
/**
* Sets the vol.
* @param vol the vol
*/
public final void setLognormalVol(final Double vol) {
Validate.isTrue(vol > 0.0 || CompareUtils.closeEquals(vol, 0.0), "Cannot set vol to be negative.");
_lognormalVol = vol;
}
/**
* Returns the member, _FwdMtm, the forward mark-to-market price. It DOES NOT compute from vol. For that, use getPrice.
* @return the fwdMtm
*/
public final Double getFwdMtm() {
return _fwdMtm;
}
/**
* Sets the forward mark-to-market price.
* @param fwdMtm forward mark-to-market price
*/
public final void setMtm(final Double fwdMtm) {
_fwdMtm = fwdMtm;
}
/**
* Sets the forward mark-to-market price.
* @param bCall True if a call, false if a put
*/
public final void setIsCall(final boolean bCall) {
_isCall = bCall;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(_expiry);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(_forward);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((_fwdMtm == null) ? 0 : _fwdMtm.hashCode());
result = prime * result + (_isCall ? 1231 : 1237);
result = prime * result + ((_lognormalVol == null) ? 0 : _lognormalVol.hashCode());
temp = Double.doubleToLongBits(_strike);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BlackFormula)) {
return false;
}
final BlackFormula other = (BlackFormula) obj;
if (Double.doubleToLongBits(_expiry) != Double.doubleToLongBits(other._expiry)) {
return false;
}
if (Double.doubleToLongBits(_forward) != Double.doubleToLongBits(other._forward)) {
return false;
}
if (Double.doubleToLongBits(_strike) != Double.doubleToLongBits(other._strike)) {
return false;
}
if (_isCall != other._isCall) {
return false;
}
if (!ObjectUtils.equals(_fwdMtm, other._fwdMtm)) {
return false;
}
if (!ObjectUtils.equals(_lognormalVol, other._lognormalVol)) {
return false;
}
return true;
}
}