/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.equity.variance.pricing;
import org.apache.commons.lang.NotImplementedException;
import com.opengamma.analytics.financial.equity.StaticReplicationDataBundle;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurface;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceDelta;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceLogMoneyness;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceMoneyness;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceStrike;
import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceVisitor;
import com.opengamma.analytics.financial.varianceswap.VarianceSwap;
import com.opengamma.analytics.math.integration.Integrator1D;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.tuple.DoublesPair;
/**
* We construct a model independent method to price variance as a static replication
* of an (in)finite sum of call and put option prices on the underlying.
* We assume the existence of a smooth function of these option prices / implied volatilities.
* The portfolio weighting is 1/k^2. As such, this method is especially sensitive to strike near zero.
* <p>
* Note: This is not intended to handle large payment delays between last observation date and payment. No convexity adjustment has been applied.
*/
public class VarianceSwapStaticReplication {
/** Calculates the expected annualised variance of an instrument with a log payoff */
private final ExpectedVarianceStaticReplicationCalculator _cal;
/**
* // * Constructor that uses the default values for expected variance calculations.
* //
*/
public VarianceSwapStaticReplication() {
_cal = new ExpectedVarianceStaticReplicationCalculator();
}
/**
* @param tolerance The tolerance of the expected variance calculations
*/
public VarianceSwapStaticReplication(final double tolerance) {
_cal = new ExpectedVarianceStaticReplicationCalculator(tolerance);
}
/**
* @param integrator The integrator to be used in expected variance calculations, not null
*/
public VarianceSwapStaticReplication(final Integrator1D<Double, Double> integrator) {
_cal = new ExpectedVarianceStaticReplicationCalculator(integrator);
}
/**
* @param integrator The integrator to be used in expected variance calculations, not null
* @param tolerance The tolerance of the expected variance calculations
*/
public VarianceSwapStaticReplication(final Integrator1D<Double, Double> integrator, final double tolerance) {
_cal = new ExpectedVarianceStaticReplicationCalculator(integrator, tolerance);
}
/**
* Calculates the present value of a variance swap using static replication
* @param deriv The variance swap, not null
* @param market Bundle containing market data, not null
* @return The present value
*/
public double presentValue(final VarianceSwap deriv, final StaticReplicationDataBundle market) {
ArgumentChecker.notNull(deriv, "VarianceSwap deriv");
ArgumentChecker.notNull(market, "EquityOptionDataBundle market");
if (deriv.getTimeToSettlement() < 0) {
return 0.0; // All payments have been settled
}
// Compute contribution from past realizations
final double realizedVar = new RealizedVariance().evaluate(deriv); // Realized variance of log returns already observed
// Compute contribution from future realizations
final double remainingVar = expectedVariance(deriv, market); // Remaining variance implied by option prices
// Compute weighting
int nObsExpected = deriv.getObsExpected(); // Expected number of observed as of trade inception
int nObsDisrupted = deriv.getObsDisrupted(); // Number of observations missed due to market disruption
double totalVar = 0.0;
if (deriv.getTimeToObsStart() > 0) { // no observations have been made
totalVar = remainingVar;
} else {
ArgumentChecker.isTrue(deriv.getObservations().length > 0, "presentValue requested after first observation date, yet no observations have been provided.");
int nObsActual = deriv.getObservations().length; // From observation start until valuation
totalVar = (realizedVar * (nObsActual - 1) + remainingVar * (nObsExpected - nObsActual - nObsDisrupted)) / (nObsExpected - 1);
}
final double finalPayment = deriv.getVarNotional() * (totalVar - deriv.getVarStrike());
final double df = market.getDiscountCurve().getDiscountFactor(deriv.getTimeToSettlement());
return df * finalPayment;
}
/**
* Computes the fair value strike of a spot starting VarianceSwap parameterised in 'variance' terms,
* It is quoted as an annual variance value, hence 1/T * integral(0,T) {sigmaSquared dt}
* <p>
*
* @param deriv VarianceSwap derivative to be priced
* @param market EquityOptionDataBundle containing volatility surface, forward underlying, and funding curve
* @return presentValue of the *remaining* variance in the swap.
*/
public double expectedVariance(final VarianceSwap deriv, final StaticReplicationDataBundle market) {
validateData(deriv, market);
final double timeToLastObs = deriv.getTimeToObsEnd();
if (timeToLastObs <= 0) { // expired swap returns 0 variance
return 0.0;
}
final double timeToFirstObs = deriv.getTimeToObsStart();
// Compute Variance from spot until last observation
final double varianceSpotEnd = expectedVarianceFromSpot(timeToLastObs, market);
// If timeToFirstObs= 0.0, the pricer will consider the volatility to be from now until timeToLastObs
final boolean forwardStarting = timeToFirstObs > 0.0;
if (!forwardStarting) {
return varianceSpotEnd;
}
final double varianceSpotStart = expectedVarianceFromSpot(timeToFirstObs, market);
return (varianceSpotEnd * timeToLastObs - varianceSpotStart * timeToFirstObs) / (timeToLastObs - timeToFirstObs);
}
private void validateData(final VarianceSwap deriv, final StaticReplicationDataBundle market) {
ArgumentChecker.notNull(deriv, "VarianceSwap deriv");
ArgumentChecker.notNull(market, "EquityOptionDataBundle market");
final double timeToLastObs = deriv.getTimeToObsEnd();
final double timeToFirstObs = deriv.getTimeToObsStart();
ArgumentChecker.isTrue(timeToFirstObs < timeToLastObs, "timeToLastObs is not sufficiently longer than timeToFirstObs");
}
/**
* Computes the fair value strike of a spot starting VarianceSwap parameterized in 'variance' terms,
* It is quoted as an annual variance value, hence 1/T * integral(0,T) {sigmaSquared dt}
* <p>
*
* @param expiry Time from spot until last observation
* @param market EquityOptionDataBundle containing volatility surface, forward underlying, and funding curve
* @return presentValue of the *remaining* variance in the swap.
*/
protected double expectedVarianceFromSpot(final double expiry, final StaticReplicationDataBundle market) {
// 1. Unpack Market data
final double fwd = market.getForwardCurve().getForward(expiry);
final BlackVolatilitySurface<?> volSurf = market.getVolatilitySurface();
final VarianceCalculator varCal = new VarianceCalculator(fwd, expiry);
return varCal.getVariance(volSurf);
}
/**
* Computes the fair value strike of a spot starting VarianceSwap parameterised in vol/vega terms.
* This is an estimate of annual Lognormal (Black) volatility
*
* @param deriv VarianceSwap derivative to be priced
* @param market EquityOptionDataBundle containing volatility surface, forward underlying, and funding curve
* @return presentValue of the *remaining* variance in the swap.
*/
public double expectedVolatility(final VarianceSwap deriv, final StaticReplicationDataBundle market) {
final double sigmaSquared = expectedVariance(deriv, market);
return Math.sqrt(sigmaSquared);
}
/**
* This is just a wrapper around ExpectedVarianceCalculator which uses a visitor pattern to farm out the calculation to the correct method of ExpectedVarianceCalculator
* depending on the type of BlackVolatilitySurface
*/
private class VarianceCalculator implements BlackVolatilitySurfaceVisitor<DoublesPair, Double> {
/** The time to expiry */
private final double _t;
/** The forward */
private final double _f;
public VarianceCalculator(final double forward, final double expiry) {
_f = forward;
_t = expiry;
}
public double getVariance(final BlackVolatilitySurface<?> surf) {
return surf.accept(this);
}
// ********************************************
// strike surfaces
// ********************************************
@Override
public Double visitStrike(final BlackVolatilitySurfaceStrike surface, final DoublesPair data) {
throw new NotImplementedException();
}
@SuppressWarnings("synthetic-access")
@Override
public Double visitStrike(final BlackVolatilitySurfaceStrike surface) {
return _cal.getAnnualizedVariance(_f, _t, surface);
}
// ********************************************
// delta surfaces
// ********************************************
@Override
public Double visitDelta(final BlackVolatilitySurfaceDelta surface, final DoublesPair data) {
throw new NotImplementedException();
}
@SuppressWarnings("synthetic-access")
@Override
public Double visitDelta(final BlackVolatilitySurfaceDelta surface) {
return _cal.getAnnualizedVariance(_f, _t, surface);
}
// ********************************************
// moneyness surfaces
// ********************************************
@Override
public Double visitMoneyness(final BlackVolatilitySurfaceMoneyness surface, final DoublesPair data) {
throw new NotImplementedException();
}
@SuppressWarnings("synthetic-access")
@Override
public Double visitMoneyness(final BlackVolatilitySurfaceMoneyness surface) {
return _cal.getAnnualizedVariance(_t, surface);
}
// ********************************************
// log-moneyness surfaces
// ********************************************
/**
* Only use if the integral limits have been calculated elsewhere, or you need the contribution from a specific range
*/
@Override
public Double visitLogMoneyness(final BlackVolatilitySurfaceLogMoneyness surface, final DoublesPair data) {
throw new NotImplementedException();
}
/**
* General method when you wish to compute the expected variance from a log-moneyness parametrised surface to within a certain tolerance
* @param surface log-moneyness parametrised volatility surface
* @return expected variance
*/
@SuppressWarnings("synthetic-access")
@Override
public Double visitLogMoneyness(final BlackVolatilitySurfaceLogMoneyness surface) {
return _cal.getAnnualizedVariance(_t, surface);
}
}
}