/** * 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 com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.SABRExtrapolationRightFunction; import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository; 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; /** * Left and right extrapolation for SABR smile interpolation based on * Benaim, S., Dodgson, M., and Kainth, D. (2008). An arbitrage-free method for smile extrapolation. * Technical report, Royal Bank of Scotland. */ public class BenaimDodgsonKainthExtrapolationFunctionProvider extends SmileExtrapolationFunctionSABRProvider { private final double _muLow; private final double _muHigh; /** * Constructor specifying parameters which control the fatness of tails * @param muLow The parameter for the left tail, must be positive * @param muHigh The parameter for the right tail, must be positive */ public BenaimDodgsonKainthExtrapolationFunctionProvider(final double muLow, final double muHigh) { ArgumentChecker.isTrue(muLow > 0.0, "muLow should be positive"); ArgumentChecker.isTrue(muHigh > 0.0, "muHigh should be positive"); _muLow = muLow; _muHigh = muHigh; } @Override public Function1D<Double, Double> getExtrapolationFunction(final SABRFormulaData sabrDataLow, final SABRFormulaData sabrDataHigh, final VolatilityFunctionProvider<SABRFormulaData> volatilityFunction, final double forward, final double expiry, final double cutOffStrikeLow, final double cutOffStrikeHigh) { ArgumentChecker.notNull(sabrDataLow, "sabrDataLow"); ArgumentChecker.notNull(sabrDataHigh, "sabrDataHigh"); ArgumentChecker.notNull(volatilityFunction, "volatilityFunction"); ArgumentChecker.isTrue(0.0 < cutOffStrikeLow, "cutOffStrikeLow should be positive"); ArgumentChecker .isTrue(cutOffStrikeLow < cutOffStrikeHigh, "cutOffStrikeLow < cutOffStrikeHigh should be satisfied"); final SABRExtrapolationLeftFunction sabrLeftExtrapolation = new SABRExtrapolationLeftFunction(forward, sabrDataLow, cutOffStrikeLow, expiry, _muLow, volatilityFunction); final SABRExtrapolationRightFunction sabrRightExtrapolation = new SABRExtrapolationRightFunction(forward, sabrDataHigh, cutOffStrikeHigh, expiry, _muHigh, volatilityFunction); return new Function1D<Double, Double>() { @Override public Double evaluate(final Double strike) { if (strike < cutOffStrikeLow) { EuropeanVanillaOption option = new EuropeanVanillaOption(strike, expiry, false); double price = sabrLeftExtrapolation.price(option); return BlackFormulaRepository.impliedVolatility(price, forward, strike, expiry, option.isCall()); } if (strike > cutOffStrikeHigh) { EuropeanVanillaOption option = new EuropeanVanillaOption(strike, expiry, true); double price = sabrRightExtrapolation.price(option); return BlackFormulaRepository.impliedVolatility(price, forward, strike, expiry, option.isCall()); } throw new OpenGammaRuntimeException( "Use smile interpolation method for cutOffStrikeLow <= strike <= cutOffStrikeHigh"); } }; } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(_muHigh); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_muLow); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof BenaimDodgsonKainthExtrapolationFunctionProvider)) { return false; } BenaimDodgsonKainthExtrapolationFunctionProvider other = (BenaimDodgsonKainthExtrapolationFunctionProvider) obj; if (Double.doubleToLongBits(_muHigh) != Double.doubleToLongBits(other._muHigh)) { return false; } if (Double.doubleToLongBits(_muLow) != Double.doubleToLongBits(other._muLow)) { return false; } return true; } }