/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.equity.variance; import static com.opengamma.financial.convention.businessday.BusinessDayDateUtils.getDaysBetween; import org.threeten.bp.LocalDate; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.instrument.InstrumentDefinitionVisitor; import com.opengamma.analytics.financial.instrument.varianceswap.VarianceSwapDefinition; import com.opengamma.analytics.util.time.TimeCalculator; import com.opengamma.financial.convention.businessday.BusinessDayDateUtils; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.timeseries.DoubleTimeSeries; import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; /** * An equity variance swap is a forward contract of the realized variance of an underlying stock or index. */ public class EquityVarianceSwapDefinition extends VarianceSwapDefinition { /** Should the dividends be corrected for when pricing */ private final boolean _correctForDividends; /** * Constructor based upon vega (volatility) parameterisation - strike and notional. * * @param obsStartDate Date of first observation, not null * @param obsEndDate Date of final observation, not null * @param settlementDate Date of cash settlement, not null * @param currency Currency of cash settlement, not null * @param calendar Specification of good business days (and holidays), not null * @param annualizationFactor Number of business days per year, not null * @param volStrike Fair value of volatility, the square root of variance, struck at trade date * @param volNotional Trade pays the difference between realized and strike variance multiplied by 0.5 * volNotional / volStrike * @param correctForDividends Whether to correct for dividends when pricing */ public EquityVarianceSwapDefinition(final ZonedDateTime obsStartDate, final ZonedDateTime obsEndDate, final ZonedDateTime settlementDate, final Currency currency, final Calendar calendar, final double annualizationFactor, final double volStrike, final double volNotional, final boolean correctForDividends) { super(obsStartDate, obsEndDate, settlementDate, currency, calendar, annualizationFactor, volStrike, volNotional); _correctForDividends = correctForDividends; } /** * Static constructor of a variance swap using a vega parameterisation of the contract. * @param obsStartDate Date of the first observation, not null * @param obsEndDate Date of the last observation, not null * @param settlementDate The settlement date, not null * @param currency The currency, not null * @param calendar The calendar used for calculating good business days, not null * @param annualizationFactor The annualisation factor * @param volStrike The volatility strike * @param volNotional The volatility notional * @param correctForDividends Whether to correct for dividends in pricing * @return The contract definition */ public static EquityVarianceSwapDefinition fromVegaParams(final ZonedDateTime obsStartDate, final ZonedDateTime obsEndDate, final ZonedDateTime settlementDate, final Currency currency, final Calendar calendar, final double annualizationFactor, final double volStrike, final double volNotional, final boolean correctForDividends) { return new EquityVarianceSwapDefinition(obsStartDate, obsEndDate, settlementDate, currency, calendar, annualizationFactor, volStrike, volNotional, correctForDividends); } /** * Static constructor of a variance swap using a variance parameterisation of the contract. * @param obsStartDate Date of the first observation, not null * @param obsEndDate Date of the last observation, not null * @param settlementDate The settlement date, not null * @param currency The currency, not null * @param calendar The calendar used for calculating good business days, not null * @param annualizationFactor The annualisation factor * @param varStrike The variance strike, not negative * @param varNotional The variance notional * @param correctForDividends Whether to correct for dividends in pricing * @return The contract definition */ public static EquityVarianceSwapDefinition fromVarianceParams(final ZonedDateTime obsStartDate, final ZonedDateTime obsEndDate, final ZonedDateTime settlementDate, final Currency currency, final Calendar calendar, final double annualizationFactor, final double varStrike, final double varNotional, final boolean correctForDividends) { ArgumentChecker.notNegative(varStrike, "variance strike"); final double volStrike = Math.sqrt(varStrike); final double volNotional = 2 * varNotional * volStrike; return new EquityVarianceSwapDefinition(obsStartDate, obsEndDate, settlementDate, currency, calendar, annualizationFactor, volStrike, volNotional, correctForDividends); } /** * Whether to correct for dividends when pricing. * @return Whether to correct for dividends */ public boolean correctForDividends() { return _correctForDividends; } @Override public EquityVarianceSwap toDerivative(final ZonedDateTime valueDate) { return toDerivative(valueDate, ImmutableLocalDateDoubleTimeSeries.EMPTY_SERIES); } /** * {@inheritDoc} The definition is responsible for constructing a view of the variance swap as of a particular date. * In particular, it resolves calendars. The VarianceSwap needs an array of observations, as well as its *expected* length. * The actual number of observations may be less than that expected at trade inception because of a market disruption event. * ( For an example of a market disruption event, see http://cfe.cboe.com/Products/Spec_VT.aspx ) * @param valueDate Date at which valuation will occur, not null * @param underlyingTimeSeries Time series of underlying observations, not null * @return VarianceSwap derivative as of date */ @Override public EquityVarianceSwap toDerivative(final ZonedDateTime valueDate, final DoubleTimeSeries<LocalDate> underlyingTimeSeries) { ArgumentChecker.notNull(valueDate, "date"); ArgumentChecker.notNull(underlyingTimeSeries, "A TimeSeries of observations must be provided. If observations have not begun, please pass an empty series."); final double timeToObsStart = TimeCalculator.getTimeBetween(valueDate, getObsStartDate()); final double timeToObsEnd = TimeCalculator.getTimeBetween(valueDate, getObsEndDate()); final double timeToSettlement = TimeCalculator.getTimeBetween(valueDate, getSettlementDate()); DoubleTimeSeries<LocalDate> realizedTS; if (timeToObsStart > 0) { realizedTS = ImmutableLocalDateDoubleTimeSeries.EMPTY_SERIES; } else { realizedTS = underlyingTimeSeries.subSeries(getObsStartDate().toLocalDate(), true, valueDate.toLocalDate(), false); } final double[] observations = realizedTS.valuesArrayFast(); final double[] observationWeights = {}; // TODO Case 2011-06-29 Calendar Add functionality for non-trivial weighting of observations ZonedDateTime finalObsDate = getObsEndDate().isAfter(valueDate) ? valueDate : getObsEndDate(); int nGoodBusinessDays = finalObsDate.isAfter(getObsStartDate()) ? BusinessDayDateUtils.getWorkingDaysInclusive(getObsStartDate(), finalObsDate, getCalendar()) : 0; final int nObsDisrupted = nGoodBusinessDays - observations.length; ArgumentChecker.isTrue(nObsDisrupted >= 0, "Have more observations {} than good business days {}", observations.length, nGoodBusinessDays); return new EquityVarianceSwap(timeToObsStart, timeToObsEnd, timeToSettlement, getVarStrike(), getVarNotional(), getCurrency(), getAnnualizationFactor(), getObsExpected(), nObsDisrupted, observations, observationWeights, correctForDividends()); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (_correctForDividends ? 1231 : 1237); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof EquityVarianceSwapDefinition)) { return false; } if (!super.equals(obj)) { return false; } final EquityVarianceSwapDefinition other = (EquityVarianceSwapDefinition) obj; if (_correctForDividends != other._correctForDividends) { return false; } return true; } @Override public <U, V> V accept(final InstrumentDefinitionVisitor<U, V> visitor, final U data) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitEquityVarianceSwapDefinition(this, data); } @Override public <V> V accept(final InstrumentDefinitionVisitor<?, V> visitor) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitEquityVarianceSwapDefinition(this); } }