/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.instrument.varianceswap; import static com.opengamma.financial.convention.businessday.BusinessDayDateUtils.getDaysBetween; import static com.opengamma.financial.convention.businessday.BusinessDayDateUtils.getWorkingDaysInclusive; import org.apache.commons.lang.ObjectUtils; import org.threeten.bp.LocalDate; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.instrument.InstrumentDefinitionVisitor; import com.opengamma.analytics.financial.instrument.InstrumentDefinitionWithData; import com.opengamma.analytics.financial.varianceswap.VarianceSwap; import com.opengamma.analytics.util.time.TimeCalculator; 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; /** * A variance swap is a forward contract on the realized variance of an underlying security. * The floating leg of a variance swap is the realized variance. */ public class VarianceSwapDefinition implements InstrumentDefinitionWithData<VarianceSwap, DoubleTimeSeries<LocalDate>> { /** The currency */ private final Currency _currency; /** The volatility strike. _varStrike := _volStrike^2 until we need something more elaborate */ private final double _volStrike; /** The volatility notional. _varNotional := 0.5 * _volNotional / _volStrike. Provides a rough estimate of the payoff if volatility realizes 1 point above strike */ private final double _volNotional; /** The variance strike. Computed internally */ private final double _varStrike; /** The variance notional. Computed internally */ private final double _varNotional; /** The variance observation period start date */ private final ZonedDateTime _obsStartDate; /** The variance observation period end date */ private final ZonedDateTime _obsEndDate; /** The settlement date */ private final ZonedDateTime _settlementDate; /** The number of observations expected given the observation dates and the holiday calendar */ private final int _nObsExpected; /** The variance annualization factor */ private final double _annualizationFactor; /** The holiday calendar */ private final Calendar _calendar; /** * 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 */ public VarianceSwapDefinition(final ZonedDateTime obsStartDate, final ZonedDateTime obsEndDate, final ZonedDateTime settlementDate, final Currency currency, final Calendar calendar, final double annualizationFactor, final double volStrike, final double volNotional) { ArgumentChecker.notNull(obsStartDate, "obsStartDate"); ArgumentChecker.notNull(obsEndDate, "obsEndDate"); ArgumentChecker.notNull(settlementDate, "settlementDate"); ArgumentChecker.notNull(currency, "currency"); ArgumentChecker.notNegativeOrZero(annualizationFactor, "annualizationFactor"); ArgumentChecker.notNull(calendar, "calendar"); _obsStartDate = obsStartDate; _obsEndDate = obsEndDate; _settlementDate = settlementDate; _currency = currency; _calendar = calendar; _nObsExpected = getWorkingDaysInclusive(obsStartDate, obsEndDate, calendar); _annualizationFactor = annualizationFactor; _volStrike = volStrike; _volNotional = volNotional; _varStrike = volStrike * volStrike; _varNotional = 0.5 * volNotional / volStrike; } /** * 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 obsFreq The observation frequency, 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 * @return The contract definition */ public static VarianceSwapDefinition 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) { return new VarianceSwapDefinition(obsStartDate, obsEndDate, settlementDate, currency, calendar, annualizationFactor, volStrike, volNotional); } /** * 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 obsFreq The observation frequency, 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 * @return The contract definition */ public static VarianceSwapDefinition 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) { ArgumentChecker.notNegative(varStrike, "variance strike"); final double volStrike = Math.sqrt(varStrike); final double volNotional = 2 * varNotional * volStrike; return new VarianceSwapDefinition(obsStartDate, obsEndDate, settlementDate, currency, calendar, annualizationFactor, volStrike, volNotional); } /** * {@inheritDoc} The definition is responsible for constructing a view of the variance swap as of a particular date. * An empty time series is used for the variance observations, and so this method should only be used * in the case where variance observation has not begun. */ @Override public VarianceSwap toDerivative(final ZonedDateTime date) { return toDerivative(date, 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 variance swap 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 ) */ @Override public VarianceSwap toDerivative(final ZonedDateTime date, final DoubleTimeSeries<LocalDate> underlyingTimeSeries) { ArgumentChecker.notNull(date, "date"); ArgumentChecker.notNull(underlyingTimeSeries, "underlyingTimeSeries"); final double timeToObsStart = TimeCalculator.getTimeBetween(date, _obsStartDate); final double timeToObsEnd = TimeCalculator.getTimeBetween(date, _obsEndDate); final double timeToSettlement = TimeCalculator.getTimeBetween(date, _settlementDate); DoubleTimeSeries<LocalDate> realizedTS; if (timeToObsStart > 0) { realizedTS = ImmutableLocalDateDoubleTimeSeries.EMPTY_SERIES; } else { realizedTS = underlyingTimeSeries.subSeries(_obsStartDate.toLocalDate(), true, date.toLocalDate(), false); } final double[] observations = realizedTS.valuesArrayFast(); final double[] observationWeights = {}; // TODO Case 2011-06-29 Calendar Add functionality for non-trivial weighting of observations //if we view this option on some date between the observation start and end dates, then the observation on that particular //date will not have been made (observations are closing levels) final int nObservations = date.isAfter(_obsEndDate) ? _nObsExpected : (date.isBefore(_obsStartDate) ? 0 : getDaysBetween(_obsStartDate, date, _calendar)); final int nObsDisrupted = nObservations - observations.length; ArgumentChecker.isTrue(nObsDisrupted >= 0, "Have more observations {} than good business days {}", observations.length, nObservations); return new VarianceSwap(timeToObsStart, timeToObsEnd, timeToSettlement, _varStrike, _varNotional, _currency, _annualizationFactor, _nObsExpected, nObsDisrupted, observations, observationWeights); } /** * Gets the first observation date. * @return the first observation date */ public ZonedDateTime getObsStartDate() { return _obsStartDate; } /** * Gets the last observation date. * @return the last observation date */ public ZonedDateTime getObsEndDate() { return _obsEndDate; } /** * Gets the settlement date. * @return the settlement date */ public ZonedDateTime getSettlementDate() { return _settlementDate; } /** * Gets the number of observations expected. This is the number of good business days as expected at trade inception. * The actual number of observations may be less if a market disruption event occurs. * @return the nObsExpected */ public int getObsExpected() { return _nObsExpected; } /** * Gets the currency. * @return the currency */ public Currency getCurrency() { return _currency; } /** * Gets the volatility strike. * @return the volatility strike */ public double getVolStrike() { return _volStrike; } /** * Gets the volatility notional. * @return the volatility notional */ public double getVolNotional() { return _volNotional; } /** * Gets the variance strike. * @return the variance strike */ public double getVarStrike() { return _varStrike; } /** * Gets the variance notional. * @return the variance notional */ public double getVarNotional() { return _varNotional; } /** * Gets the calendar. * @return the calendar */ public Calendar getCalendar() { return _calendar; } /** * Gets the annualization factor. * @return The annualization factor */ public double getAnnualizationFactor() { return _annualizationFactor; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + _currency.hashCode(); result = prime * result + _obsEndDate.hashCode(); result = prime * result + _obsStartDate.hashCode(); result = prime * result + _settlementDate.hashCode(); result = prime * result + _calendar.hashCode(); long temp; temp = Double.doubleToLongBits(_volNotional); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_volStrike); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_annualizationFactor); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof VarianceSwapDefinition)) { return false; } final VarianceSwapDefinition other = (VarianceSwapDefinition) obj; if (Double.compare(_volStrike, other._volStrike) != 0) { return false; } if (Double.compare(_volNotional, other._volNotional) != 0) { return false; } if (Double.compare(_annualizationFactor, other._annualizationFactor) != 0) { return false; } if (!(ObjectUtils.equals(_obsStartDate, other._obsStartDate))) { return false; } if (!(ObjectUtils.equals(_obsEndDate, other._obsEndDate))) { return false; } if (!(ObjectUtils.equals(_settlementDate, other._settlementDate))) { return false; } if (!(ObjectUtils.equals(_currency, other._currency))) { return false; } if (!(ObjectUtils.equals(_calendar, other._calendar))) { return false; } return true; } @Override public <U, V> V accept(final InstrumentDefinitionVisitor<U, V> visitor, final U data) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitVarianceSwapDefinition(this, data); } @Override public <V> V accept(final InstrumentDefinitionVisitor<?, V> visitor) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitVarianceSwapDefinition(this); } }