/** * 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; } }