/** * 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 static org.testng.AssertJUnit.assertEquals; import java.util.Arrays; import java.util.List; import org.testng.annotations.Test; import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve; import com.opengamma.analytics.financial.model.volatility.discrete.ParameterizedSABRModelDiscreteVolatilityFunctionProvider; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.function.DoublesVectorFunctionProvider; import com.opengamma.analytics.math.function.InterpolatedVectorFunctionProvider; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.analytics.math.interpolation.TransformedInterpolator1D; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.analytics.math.minimization.DoubleRangeLimitTransform; import com.opengamma.analytics.math.minimization.ParameterLimitsTransform; import com.opengamma.analytics.math.minimization.ParameterLimitsTransform.LimitType; import com.opengamma.analytics.math.minimization.SingleRangeLimitTransform; import com.opengamma.util.test.TestGroup; /** * This tests {@link CapletStripperSABRModel} where the parameter term structures are represented at interpolated curves */ @Test(groups=TestGroup.UNIT) public class CapletStripperSABRModelTest extends CapletStrippingSetup { private static final double[] ALPHA_KNOTS = new double[] {1, 2, 3, 5, 7, 10 }; private static final double[] BETA_KNOTS = new double[] {1 }; private static final double[] RHO_KNOTS = new double[] {1, 3, 7 }; private static final double[] NU_KNOTS = new double[] {1, 2, 3, 5, 7, 10 }; private static final Interpolator1D BASE_INTERPOLATOR; private static final ParameterLimitsTransform ALPHA_TRANSFORM; private static final ParameterLimitsTransform BETA_TRANSFORM; private static final ParameterLimitsTransform RHO_TRANSFORM; private static final ParameterLimitsTransform NU_TRANSFORM; private static final DoubleMatrix1D START; private static final DoublesVectorFunctionProvider[] s_providers; static { BASE_INTERPOLATOR = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.DOUBLE_QUADRATIC, Interpolator1DFactory.LINEAR_EXTRAPOLATOR); ALPHA_TRANSFORM = new SingleRangeLimitTransform(0.0, LimitType.GREATER_THAN); BETA_TRANSFORM = new DoubleRangeLimitTransform(0.1, 1); RHO_TRANSFORM = new DoubleRangeLimitTransform(-1, 1); NU_TRANSFORM = new SingleRangeLimitTransform(0.0, LimitType.GREATER_THAN); int nAlphaKnots = ALPHA_KNOTS.length; int nBetaKnots = BETA_KNOTS.length; int nRhoKnots = RHO_KNOTS.length; int nNuKnots = NU_KNOTS.length; START = new DoubleMatrix1D(nAlphaKnots + +nBetaKnots + nRhoKnots + nNuKnots); double[] temp = new double[nAlphaKnots]; Arrays.fill(temp, ALPHA_TRANSFORM.transform(0.2)); System.arraycopy(temp, 0, START.getData(), 0, nAlphaKnots); temp = new double[nBetaKnots]; Arrays.fill(temp, BETA_TRANSFORM.transform(0.7)); System.arraycopy(temp, 0, START.getData(), nAlphaKnots, nBetaKnots); temp = new double[nRhoKnots]; Arrays.fill(temp, RHO_TRANSFORM.transform(-0.2)); System.arraycopy(temp, 0, START.getData(), nAlphaKnots + nBetaKnots, nRhoKnots); temp = new double[nNuKnots]; Arrays.fill(temp, NU_TRANSFORM.transform(0.5)); System.arraycopy(temp, 0, START.getData(), nAlphaKnots + nBetaKnots + nRhoKnots, nNuKnots); InterpolatedVectorFunctionProvider alphaPro = new InterpolatedVectorFunctionProvider(new TransformedInterpolator1D(BASE_INTERPOLATOR, ALPHA_TRANSFORM), ALPHA_KNOTS); InterpolatedVectorFunctionProvider betaPro = new InterpolatedVectorFunctionProvider(new TransformedInterpolator1D(CombinedInterpolatorExtrapolatorFactory.getInterpolator( Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR), BETA_TRANSFORM), BETA_KNOTS); InterpolatedVectorFunctionProvider rhoPro = new InterpolatedVectorFunctionProvider(new TransformedInterpolator1D(BASE_INTERPOLATOR, RHO_TRANSFORM), RHO_KNOTS); InterpolatedVectorFunctionProvider nuPro = new InterpolatedVectorFunctionProvider(new TransformedInterpolator1D(BASE_INTERPOLATOR, NU_TRANSFORM), NU_KNOTS); s_providers = new DoublesVectorFunctionProvider[] {alphaPro, betaPro, rhoPro, nuPro }; } /** * Fit all the cap (109 including ATM) using the SABR model with parameters given by interpolated term structures. The * fit is * poor (RMS error around 93bps), but is does produce a very smooth (caplet) volatility surface. */ @Test public void allCapsTest() { double oneBP = 1e-4; MulticurveProviderDiscount yc = getYieldCurves(); List<CapFloor> caps = getAllCaps(); int nCaps = caps.size(); double[] vols = getAllCapVols(); MultiCapFloorPricer pricer = new MultiCapFloorPricer(caps, yc); double[] errors = new double[nCaps]; Arrays.fill(errors, oneBP); // 1bps CapletStripper stripper = new CapletStripperSABRModel(pricer, s_providers); CapletStrippingResult res = stripper.solve(vols, MarketDataType.VOL, errors, START); double expectedChi2 = 936380.0252991668; // this corresponds to a RMS errors of about 93bps assertEquals(expectedChi2, res.getChiSqr(), 1e-9 * expectedChi2); } /** * Fit all the cap prices (weighted by vega) (109 including ATM) using the SABR model with parameters given by * interpolated term structures. The fit is * poor (RMS error around 93bps), but is does produce a very smooth (caplet) volatility surface. */ @Test public void allCapsPriceTest() { double oneBP = 1e-4; MulticurveProviderDiscount yc = getYieldCurves(); List<CapFloor> caps = getAllCaps(); int nCaps = caps.size(); double[] vols = getAllCapVols(); MultiCapFloorPricer pricer = new MultiCapFloorPricer(caps, yc); double[] pricers = pricer.price(vols); double[] vega = pricer.vega(vols); double[] errors = new double[nCaps]; Arrays.fill(errors, oneBP); // 1bps CapletStripper stripper = new CapletStripperSABRModel(pricer, s_providers); // Fit to price (weighted by vega) // scale vega for (int i = 0; i < nCaps; i++) { vega[i] *= oneBP; } CapletStrippingResult res = stripper.solve(pricers, MarketDataType.PRICE, vega, START); double expectedChi2 = 925326.9058053035; assertEquals(expectedChi2, res.getChiSqr(), 1e-8 * expectedChi2); } /** * Fit all the cap excluding the ATM (102) using the SABR model with parameters given by interpolated term structures. * The fit is * poor (RMS error around 82bps), but is does produce a very smooth (caplet) volatility surface. */ @Test public void allCapsExATMTest() { MulticurveProviderDiscount yc = getYieldCurves(); List<CapFloor> caps = getAllCapsExATM(); double[] vols = getAllCapVolsExATM(); MultiCapFloorPricer pricer = new MultiCapFloorPricer(caps, yc); CapletStripper stripper = new CapletStripperSABRModel(pricer, s_providers); // error is effectively 10,000bps CapletStrippingResult res = stripper.solve(vols, MarketDataType.VOL, START); double expectedChi2 = 0.006812970733200472; // this corresponds to a RMS errors of about 82bps assertEquals(expectedChi2, res.getChiSqr(), 1e-9 * expectedChi2); } @Test public void providerTest() { MulticurveProviderDiscount yc = getYieldCurves(); List<CapFloor> caps = getAllCaps(); double[] vols = getAllCapVols(); MultiCapFloorPricer pricer = new MultiCapFloorPricer(caps, yc); double[] capletExpiries = pricer.getCapletExpiries(); double[] fwds = pricer.getCapletForwardRates(); // this interpolated forward curve that will only be hit at the knots, so don't need anything more than linear ForwardCurve fwdCurve = new ForwardCurve(InterpolatedDoublesCurve.from(capletExpiries, fwds, CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR))); ParameterizedSABRModelDiscreteVolatilityFunctionProvider dvfp = new ParameterizedSABRModelDiscreteVolatilityFunctionProvider(fwdCurve, s_providers); CapletStripper stripper = new CapletStripperSABRModel(pricer, dvfp); CapletStrippingResult res = stripper.solve(vols, MarketDataType.VOL, START); double expectedChi2 = 0.009363800283928515; // this corresponds to a RMS errors of about 93bps assertEquals(expectedChi2, res.getChiSqr(), 1e-9 * expectedChi2); } }