/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.interestrate.capletstripping; import java.util.Arrays; import com.opengamma.analytics.financial.model.volatility.discrete.DiscreteVolatilityFunctionProvider; import com.opengamma.analytics.financial.model.volatility.discrete.DiscreteVolatilityFunctionProviderDirect; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.interpolation.PenaltyMatrixGenerator; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.analytics.math.matrix.DoubleMatrix2D; import com.opengamma.analytics.math.minimization.PositiveOrZero; import com.opengamma.util.ArgumentChecker; /** * Fit directly for caplet volatilities using a (penalty) matrix which imposes a penalty for curvature of the volatility * 'surface' (actually the strike-expiry grid of caplet volatilities) */ public class CapletStripperDirect implements CapletStripper { private static final Function1D<DoubleMatrix1D, Boolean> POSITIVE = new PositiveOrZero(); private final MultiCapFloorPricerGrid _pricer; private final double _lambdaT; private final double _lambdaK; /** * Set up the stripper * @param pricer The pricer (which contained the details of the market values of the caps/floors) * @param lambda Use the same curvature penalty in both strike and expiry directions. A lower value will impose less * constraint, allowing better recovery of cap values, but a less smooth volatility surface */ public CapletStripperDirect(MultiCapFloorPricerGrid pricer, double lambda) { ArgumentChecker.notNull(pricer, "pricer"); ArgumentChecker.notNegative(lambda, "lambda"); _pricer = pricer; _lambdaT = lambda; _lambdaK = lambda; } /** * Set up the stripper * @param pricer The pricer * @param lambdaK the curvature penalty in the strike direction * @param lambdaT the curvature penalty in the expiry direction */ public CapletStripperDirect(MultiCapFloorPricerGrid pricer, double lambdaK, double lambdaT) { ArgumentChecker.notNull(pricer, "pricer"); ArgumentChecker.notNegative(lambdaT, "lambdaT"); ArgumentChecker.notNegative(lambdaK, "lambdaK"); _pricer = pricer; _lambdaT = lambdaT; _lambdaK = lambdaK; } @Override public CapletStrippingResult solve(double[] marketValues, MarketDataType type) { ArgumentChecker.notNull(marketValues, "marketValues"); int n = marketValues.length; double[] errors = new double[n]; Arrays.fill(errors, 1.0); DoubleMatrix1D guess = new DoubleMatrix1D(_pricer.getNumCaplets(), 0.4); return solve(marketValues, type, errors, guess); } @Override public CapletStrippingResult solve(double[] marketValues, MarketDataType type, double[] errors) { DoubleMatrix1D guess = new DoubleMatrix1D(_pricer.getNumCaplets(), 0.4); return solve(marketValues, type, errors, guess); } @Override public CapletStrippingResult solve(double[] marketValues, MarketDataType type, DoubleMatrix1D guess) { ArgumentChecker.notNull(marketValues, "marketValues"); int n = marketValues.length; double[] errors = new double[n]; Arrays.fill(errors, 1.0); return solve(marketValues, type, errors, guess); } @Override public CapletStrippingResult solve(double[] marketValues, MarketDataType type, double[] errors, DoubleMatrix1D guess) { DoubleMatrix2D p = getPenaltyMatrix(_pricer.getStrikes(), _pricer.getCapletExpiries(), _lambdaK, _lambdaT); CapletStrippingCore imp = getImp(marketValues); if (type == MarketDataType.PRICE) { return imp.solveForCapPrices(marketValues, errors, guess, p, POSITIVE); } else if (type == MarketDataType.VOL) { return imp.solveForCapVols(marketValues, errors, guess, p, POSITIVE); } throw new IllegalArgumentException("Unknown MarketDataType " + type.toString()); } private CapletStrippingCore getImp(double[] values) { ArgumentChecker.notEmpty(values, "values"); int nCaps = _pricer.getNumCaps(); ArgumentChecker.isTrue(nCaps == values.length, "Expected {} cap prices, but only given {}", nCaps, values.length); DiscreteVolatilityFunctionProvider volPro = new DiscreteVolatilityFunctionProviderDirect(); return new CapletStrippingCore(_pricer, volPro); } /** * get the penalty matrix. Note: this has protected access for testing * @param strikes array of strikes * @param expiries array of expiries * @param lambdaK the curvature penalty in the strike direction * @param lambdaT the curvature penalty in the expiry direction * @return The penalty matrix */ protected DoubleMatrix2D getPenaltyMatrix(double[] strikes, double[] expiries, double lambdaK, double lambdaT) { // use second order difference unless too few points int diffOrderK = Math.min(2, strikes.length - 1); int diffOrderT = Math.min(2, expiries.length - 1); double effLambdaK = diffOrderK == 0 ? 0.0 : lambdaK; double effLambdaT = diffOrderT == 0 ? 0.0 : lambdaT; return PenaltyMatrixGenerator.getPenaltyMatrix(new double[][] {strikes, expiries }, new int[] {diffOrderK, diffOrderT }, new double[] {effLambdaK, effLambdaT }); } }