/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.volatility.smile.fitting.interpolation; import java.util.List; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption; import com.opengamma.analytics.financial.model.volatility.smile.function.SABRFormulaData; import com.opengamma.analytics.financial.model.volatility.smile.function.VolatilityFunctionProvider; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.util.ArgumentChecker; /** * SABR smile interpolator with left and right extrapolation by using {@link SmileExtrapolationFunctionSABRProvider} */ public class SmileInterpolatorSABRWithExtrapolation extends SmileInterpolatorSABR { private final SmileExtrapolationFunctionSABRProvider _extrapolationFunctionProvider; /** * Constructor specifying extrapolation method * @param extrapolationFunctionProvider The extrapolation method */ public SmileInterpolatorSABRWithExtrapolation( final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider) { ArgumentChecker.notNull(extrapolationFunctionProvider, "extrapolationFunctionProvider"); _extrapolationFunctionProvider = extrapolationFunctionProvider; } /** * Constructor specifying volatility formula extrapolation method * @param formula The volatility formula * @param extrapolationFunctionProvider The extrapolation method */ public SmileInterpolatorSABRWithExtrapolation(final VolatilityFunctionProvider<SABRFormulaData> formula, final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider) { super(formula); ArgumentChecker.notNull(extrapolationFunctionProvider, "extrapolationFunctionProvider"); _extrapolationFunctionProvider = extrapolationFunctionProvider; } /** * Constructor specifying seed, volatility formula, beta, weight function and extrapolation method, * see {@link SmileInterpolatorSABR} for the parameter detail * @param seed The seed * @param formula The volatility formula * @param beta The beta parameter * @param weightFunction The weight function * @param extrapolationFunctionProvider The extrapolation method */ public SmileInterpolatorSABRWithExtrapolation(final int seed, final VolatilityFunctionProvider<SABRFormulaData> formula, final double beta, final WeightingFunction weightFunction, final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider) { super(seed, formula, beta, weightFunction); ArgumentChecker.notNull(extrapolationFunctionProvider, "extrapolationFunctionProvider"); _extrapolationFunctionProvider = extrapolationFunctionProvider; } @Override public Function1D<Double, Double> getVolatilityFunction(final double forward, final double[] strikes, final double expiry, final double[] impliedVols) { ArgumentChecker.notNull(strikes, "strikes"); ArgumentChecker.notNull(impliedVols, "impliedVols"); ArgumentChecker.isTrue(strikes.length == impliedVols.length, "strikes and impliedVols should have the same length"); final int nStrikes = strikes.length; final double cutOffStrikeLow = strikes[0]; final double cutOffStrikeHigh = strikes[nStrikes - 1]; SABRFormulaData sabrDataLow; SABRFormulaData sabrDataHigh; final List<SABRFormulaData> modelParams; final int n; List<SABRFormulaData> modelParamsTmp; int nTmp; try { modelParamsTmp = getFittedModelParameters(forward, strikes, expiry, impliedVols); nTmp = strikes.length; int ref = Math.max(0, nTmp - 3); sabrDataLow = modelParamsTmp.get(0); sabrDataHigh = modelParamsTmp.get(ref); } catch (final Exception e) { //try global fit if local fit failed nTmp = 1; modelParamsTmp = getFittedModelParametersGlobal(forward, strikes, expiry, impliedVols); sabrDataLow = modelParamsTmp.get(0); sabrDataHigh = modelParamsTmp.get(0); } modelParams = modelParamsTmp; n = nTmp; final Function1D<Double, Double> interpFunc; if (n == 1) { interpFunc = new Function1D<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final Double strike) { final EuropeanVanillaOption option = new EuropeanVanillaOption(strike, expiry, true); final Function1D<SABRFormulaData, Double> volFunc = getModel().getVolatilityFunction(option, forward); return volFunc.evaluate(modelParams.get(0)); } }; } else { interpFunc = getVolatilityFunctionFromModelParameters(forward, strikes, expiry, modelParams); } final Function1D<Double, Double> extrapFunc = _extrapolationFunctionProvider.getExtrapolationFunction(sabrDataLow, sabrDataHigh, getModel(), forward, expiry, cutOffStrikeLow, cutOffStrikeHigh); return new Function1D<Double, Double>() { @Override public Double evaluate(final Double strike) { ArgumentChecker.notNegative(strike, "strike"); if (strike < cutOffStrikeLow) { return extrapFunc.evaluate(strike); } if (strike > cutOffStrikeHigh) { return extrapFunc.evaluate(strike); } return interpFunc.evaluate(strike); } }; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((_extrapolationFunctionProvider == null) ? 0 : _extrapolationFunctionProvider.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (!(obj instanceof SmileInterpolatorSABRWithExtrapolation)) { return false; } SmileInterpolatorSABRWithExtrapolation other = (SmileInterpolatorSABRWithExtrapolation) obj; if (_extrapolationFunctionProvider == null) { if (other._extrapolationFunctionProvider != null) { return false; } } else if (!_extrapolationFunctionProvider.equals(other._extrapolationFunctionProvider)) { return false; } return true; } }