/** * 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.pricing; import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository; import com.opengamma.analytics.financial.model.volatility.smile.fitting.sabr.SmileSurfaceDataBundle; import com.opengamma.analytics.financial.model.volatility.smile.fitting.sabr.StandardSmileSurfaceDataBundle; import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceMoneyness; import com.opengamma.analytics.financial.model.volatility.surface.PureImpliedVolatilitySurface; import com.opengamma.analytics.financial.model.volatility.surface.VolatilitySurfaceInterpolator; import com.opengamma.util.ArgumentChecker; /** * Converts an volatility surface where the prices (and so the implied volatilities) include information about dividends to a pure implied volatility * surface, where the effect of dividends has been removed. */ public class EquityVolatilityToPureVolatilitySurfaceConverter { /** * @param spot The spot, greater than zero * @param discountCurve The discount curve, not null * @param dividends The dividends, not null * @param expiries The expiries, not null * @param strikes The strikes, not null. Must have the same number of strikes as expiries. * @param otmPrices The out of the money prices, not null. Must have the same number of price strips as expiries, with each price strip being * the same length as the strikes. * @param surfaceInterpolator The volatility surface interpolator, not null * @return A pure implied volatility surface */ public static PureImpliedVolatilitySurface getConvertedSurface(final double spot, final YieldAndDiscountCurve discountCurve, final AffineDividends dividends, final double[] expiries, final double[][] strikes, final double[][] otmPrices, final VolatilitySurfaceInterpolator surfaceInterpolator) { ArgumentChecker.notNull(discountCurve, "discount curve"); ArgumentChecker.notNull(dividends, "dividends"); ArgumentChecker.notNull(expiries, "expiries"); final int nExp = expiries.length; ArgumentChecker.isTrue(strikes.length == nExp, "number of strike strips ({}) not equal to number of expiries({})", strikes.length, nExp); ArgumentChecker.isTrue(otmPrices.length == nExp, "number of price strips ({}) not equal to number of expiries({})", strikes.length, nExp); for (int i = 0; i < nExp; i++) { ArgumentChecker.isTrue(strikes[i].length == otmPrices[i].length, "number of prices and strikes in strip #{} (expiry = {}) do not match. {} prices and {} strikes", i, expiries[i], otmPrices[i].length, strikes[i].length); } //convert the real option prices to prices of options on pure stock, then find the implied volatility of these options final EquityDividendsCurvesBundle divCurves = new EquityDividendsCurvesBundle(spot, discountCurve, dividends); final double[][] x = new double[nExp][]; final double[][] vols = new double[nExp][]; for (int i = 0; i < nExp; i++) { final double t = expiries[i]; final double f = divCurves.getF(t); final double d = divCurves.getD(t); final double p = discountCurve.getDiscountFactor(t); final int n = strikes[i].length; x[i] = new double[n]; vols[i] = new double[n]; for (int j = 0; j < n; j++) { final boolean isCall = strikes[i][j] >= f; final double temp = strikes[i][j] - d; ArgumentChecker.isTrue(temp >= 0, "strike of {} at expiry {} is less than the discounted value of future cash dividends {}. Either remove this option or change the dividend assumption", strikes[i][j], t, d); x[i][j] = temp / (f - d); final double purePrice = otmPrices[i][j] / p / (f - d); vols[i][j] = BlackFormulaRepository.impliedVolatility(purePrice, 1.0, x[i][j], t, isCall); } } //fit an implied volatility surface to the pure implied vols (as the forward is 1.0, the BlackVolatilitySurfaceMoneyness is numerically identical to the PureImpliedVolatilitySurface final SmileSurfaceDataBundle data = new StandardSmileSurfaceDataBundle(new ForwardCurve(1.0), expiries, x, vols); final BlackVolatilitySurfaceMoneyness surf = surfaceInterpolator.getVolatilitySurface(data); return new PureImpliedVolatilitySurface(surf.getSurface()); //TODO have a direct fitter for PureImpliedVolatilitySurface } }