/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.varianceswap; import java.util.Arrays; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitor; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; /** * A variance swap is a forward contract on the realised variance of a generic underlying. This could be a single equity price, the value of an equity index, * an FX rate or <b>any</b> other financial metric on which a variance swap contract is based. * <p> * The floating leg of a variance swap is the realized variance and is calculated using the second moment of log returns of the underlying asset. * <p> * Because variance is additive in time, the value of a variance swap can be decomposed at any point in time between realized and implied variance as _varNotional * Z(t,T) * [ t/T * RealizedVol(0,t)^2 * + (T-t)/T * ImpliedVol(t,T)^2 - volStrike^2 ] */ public class VarianceSwap implements InstrumentDerivative { /** The time in years to the start of variance observations */ private final double _timeToObsStart; /** The time in years to the end of variance observations */ private final double _timeToObsEnd; /** The time year years to settlement */ private final double _timeToSettlement; /** The variance strike. volStrike ^ 2 */ private final double _varStrike; /** The variance notional. 0.5 * _volNotional / _volStrike */ private final double _varNotional; /** The currency */ private final Currency _currency; /** The annualization factor */ private final double _annualizationFactor; // typically 252 with daily observations /** The number of expected observations */ private final int _nObsExpected; /** The number of missing observations */ private final int _nObsDisrupted; /** The observed variances */ private final double[] _observations; /** The observation weights */ private final double[] _observationWeights; /** * @param timeToObsStart Time of first observation. Negative if observations have begun. * @param timeToObsEnd Time of final observation. Negative if observations have finished. * @param timeToSettlement Time of cash settlement. If negative, the swap has expired. * @param varStrike Fair value of Variance struck at trade date * @param varNotional Trade pays the difference between realized and strike variance multiplied by this * @param currency Currency of cash settlement * @param annualizationFactor Number of business days per year * @param nObsExpected Number of observations expected as of trade inception * @param nObsDisrupted Number of expected observations that did not occur because of a market disruption * @param observations Array of observations of the underlying spot * @param observationWeights Array of weights to give observation returns. If null, all weights are 1. Else, length must be: observations.length-1 */ public VarianceSwap(final double timeToObsStart, final double timeToObsEnd, final double timeToSettlement, final double varStrike, final double varNotional, final Currency currency, final double annualizationFactor, final int nObsExpected, final int nObsDisrupted, final double[] observations, final double[] observationWeights) { ArgumentChecker.isTrue(varStrike >= 0, "Require varStrike >= 0"); ArgumentChecker.isTrue(varNotional > 0, "Require varNotional > 0"); ArgumentChecker.notNull(currency, "currency"); ArgumentChecker.isTrue(annualizationFactor > 0, "Require annualizationFactor > 0"); ArgumentChecker.isTrue(nObsExpected > 0, "Encountered a VarianceSwap with 0 expected observations"); ArgumentChecker.notNull(observations, "observations"); ArgumentChecker.notNull(observationWeights, "observationWeights"); ArgumentChecker.isTrue(nObsExpected >= observations.length, "Number of observations ({}) great than expected ({})", observations.length, nObsExpected); _timeToObsStart = timeToObsStart; _timeToObsEnd = timeToObsEnd; _timeToSettlement = timeToSettlement; _varStrike = varStrike; _varNotional = varNotional; _currency = currency; _annualizationFactor = annualizationFactor; _nObsExpected = nObsExpected; _nObsDisrupted = nObsDisrupted; _observations = observations.clone(); _observationWeights = observationWeights.clone(); if (_observationWeights.length > 1) { final int nWeights = _observationWeights.length; final int nObs = _observations.length; ArgumentChecker.isTrue(nWeights + 1 == nObs, "If provided, observationWeights must be of length one less than observations, as they weight returns log(obs[i]/obs[i-1])." + " Found {} weights and {} observations.", nWeights, nObs); } } /** * Copy constructor * @param other VarianceSwap to copy from */ public VarianceSwap(final VarianceSwap other) { ArgumentChecker.notNull(other, "variance swap to copy"); _timeToObsStart = other._timeToObsStart; _timeToObsEnd = other._timeToObsEnd; _timeToSettlement = other._timeToSettlement; _varStrike = other._varStrike; _varNotional = other._varNotional; _currency = other._currency; _annualizationFactor = other._annualizationFactor; _nObsExpected = other._nObsExpected; _nObsDisrupted = other._nObsDisrupted; _observations = Arrays.copyOf(other._observations, other._observations.length); _observationWeights = Arrays.copyOf(other._observationWeights, other._observationWeights.length); } /** * Gets the timeToObsStart. * @return the timeToObsStart */ public double getTimeToObsStart() { return _timeToObsStart; } /** * Gets the timeToObsEnd. * @return the timeToObsEnd */ public double getTimeToObsEnd() { return _timeToObsEnd; } /** * Gets the timeToSettlement. * @return the timeToSettlement */ public double getTimeToSettlement() { return _timeToSettlement; } /** * Gets the nObsExpected. * @return the nObsExpected */ public int getObsExpected() { return _nObsExpected; } /** * Gets the nObsDisrupted. * @return the nObsDisrupted */ public int getObsDisrupted() { return _nObsDisrupted; } /** * Gets the currency. * @return the currency */ public Currency getCurrency() { return _currency; } /** * Gets the varStrike. * @return the varStrike */ public double getVarStrike() { return _varStrike; } /** * Gets the varNotional. * @return the varNotional */ public double getVarNotional() { return _varNotional; } /** * Gets the volStrike. * @return the volStrike */ public double getVolStrike() { return Math.sqrt(_varStrike); } /** * Gets the volNotional. * @return the volNotional */ public double getVolNotional() { return _varNotional * 2 * Math.sqrt(_varStrike); } /** * Gets the annualizationFactor. * @return the annualizationFactor */ public double getAnnualizationFactor() { return _annualizationFactor; } /** * Gets the observations. * @return the observations */ public double[] getObservations() { return _observations; } /** * Gets the observationWeights. * @return the observationWeights */ public final double[] getObservationWeights() { return _observationWeights; } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(_annualizationFactor); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + ((_currency == null) ? 0 : _currency.hashCode()); result = prime * result + _nObsDisrupted; result = prime * result + _nObsExpected; result = prime * result + ((_observations == null) ? 0 : Arrays.hashCode(_observations)); result = prime * result + ((_observationWeights == null) ? 0 : Arrays.hashCode(_observations)); temp = Double.doubleToLongBits(_timeToObsEnd); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_timeToObsStart); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_timeToSettlement); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_varNotional); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_varStrike); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof VarianceSwap)) { return false; } final VarianceSwap other = (VarianceSwap) obj; if (Double.doubleToLongBits(_annualizationFactor) != Double.doubleToLongBits(other._annualizationFactor)) { return false; } if (_currency == null) { if (other._currency != null) { return false; } } else if (!_currency.equals(other._currency)) { return false; } if (_nObsDisrupted != other._nObsDisrupted) { return false; } if (_nObsExpected != other._nObsExpected) { return false; } if (!Arrays.equals(_observationWeights, other._observationWeights)) { return false; } if (!Arrays.equals(_observations, other._observations)) { return false; } if (Double.doubleToLongBits(_timeToObsEnd) != Double.doubleToLongBits(other._timeToObsEnd)) { return false; } if (Double.doubleToLongBits(_timeToObsStart) != Double.doubleToLongBits(other._timeToObsStart)) { return false; } if (Double.doubleToLongBits(_timeToSettlement) != Double.doubleToLongBits(other._timeToSettlement)) { return false; } if (Double.doubleToLongBits(_varNotional) != Double.doubleToLongBits(other._varNotional)) { return false; } if (Double.doubleToLongBits(_varStrike) != Double.doubleToLongBits(other._varStrike)) { return false; } return true; } @Override public <S, T> T accept(final InstrumentDerivativeVisitor<S, T> visitor, final S data) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitVarianceSwap(this, data); } @Override public <T> T accept(final InstrumentDerivativeVisitor<?, T> visitor) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitVarianceSwap(this); } }