//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption product to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the /// curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential /// re-calibration of the model parameters to the raw market data. /// /// </para> /// </summary> /// <param name="swaption"> the swaption product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyModel(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); ZonedDateTime expiryDateTime = swaption.Expiry; double expiry = swaptionVolatilities.relativeTime(expiryDateTime); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); ValueDerivatives annuityDerivative = SwapPricer.LegPricer.annuityCashDerivative(fixedLeg, forward); double annuityCash = annuityDerivative.Value; double annuityCashDr = annuityDerivative.getDerivative(0); LocalDate settlementDate = ((CashSwaptionSettlement)swaption.SwaptionSettlement).SettlementDate; double discountSettle = ratesProvider.discountFactor(fixedLeg.Currency, settlementDate); double strike = calculateStrike(fixedLeg); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double shift = swaptionVolatilities.shift(expiry, tenor); ValueDerivatives volatilityAdj = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward); bool isCall = fixedLeg.PayReceive.Pay; double shiftedForward = forward + shift; double shiftedStrike = strike + shift; double price = BlackFormulaRepository.price(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall); double delta = BlackFormulaRepository.delta(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall); double vega = BlackFormulaRepository.vega(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value); PointSensitivityBuilder forwardSensi = SwapPricer.parRateSensitivity(underlying, ratesProvider); PointSensitivityBuilder discountSettleSensi = ratesProvider.discountFactors(fixedLeg.Currency).zeroRatePointSensitivity(settlementDate); double sign = swaption.LongShort.sign(); return(forwardSensi.multipliedBy(sign * discountSettle * (annuityCash * (delta + vega * volatilityAdj.getDerivative(0)) + annuityCashDr * price)).combinedWith(discountSettleSensi.multipliedBy(sign * annuityCash * price))); }
//------------------------------------------------------------------------- public virtual void test_presentValueSensitivityVolatility() { PointSensitivities pointCaplet = PRICER.presentValueSensitivityModelParamsSabr(CAPLET_LONG, RATES, VOLS).build(); PointSensitivities pointFloorlet = PRICER.presentValueSensitivityModelParamsSabr(FLOORLET_SHORT, RATES, VOLS).build(); double forward = RATES.iborIndexRates(EUR_EURIBOR_3M).rate(RATE_COMP.Observation); double expiry = VOLS.relativeTime(CAPLET_LONG.FixingDateTime); ValueDerivatives volSensi = VOLS.Parameters.volatilityAdjoint(expiry, STRIKE, forward); double df = RATES.discountFactor(EUR, CAPLET_LONG.PaymentDate); double vegaCaplet = NOTIONAL * df * CAPLET_LONG.YearFraction * BlackFormulaRepository.vega(forward + SHIFT, STRIKE + SHIFT, expiry, volSensi.Value); double vegaFloorlet = -NOTIONAL *df *CAPLET_LONG.YearFraction *BlackFormulaRepository.vega(forward + SHIFT, STRIKE + SHIFT, expiry, volSensi.Value); assertSensitivity(pointCaplet, SabrParameterType.ALPHA, vegaCaplet * volSensi.getDerivative(2), TOL); assertSensitivity(pointCaplet, SabrParameterType.BETA, vegaCaplet * volSensi.getDerivative(3), TOL); assertSensitivity(pointCaplet, SabrParameterType.RHO, vegaCaplet * volSensi.getDerivative(4), TOL); assertSensitivity(pointCaplet, SabrParameterType.NU, vegaCaplet * volSensi.getDerivative(5), TOL); assertSensitivity(pointFloorlet, SabrParameterType.ALPHA, vegaFloorlet * volSensi.getDerivative(2), TOL); assertSensitivity(pointFloorlet, SabrParameterType.BETA, vegaFloorlet * volSensi.getDerivative(3), TOL); assertSensitivity(pointFloorlet, SabrParameterType.RHO, vegaFloorlet * volSensi.getDerivative(4), TOL); assertSensitivity(pointFloorlet, SabrParameterType.NU, vegaFloorlet * volSensi.getDerivative(5), TOL); PointSensitivities pointCapletVol = PRICER.presentValueSensitivityModelParamsVolatility(CAPLET_LONG, RATES, VOLS).build(); // vol sensitivity in base class PointSensitivities pointFloorletVol = PRICER.presentValueSensitivityModelParamsVolatility(FLOORLET_SHORT, RATES, VOLS).build(); IborCapletFloorletSensitivity pointCapletVolExp = IborCapletFloorletSensitivity.of(VOLS.Name, expiry, STRIKE, forward, EUR, vegaCaplet); IborCapletFloorletSensitivity pointFloorletVolExp = IborCapletFloorletSensitivity.of(VOLS.Name, expiry, STRIKE, forward, EUR, vegaFloorlet); assertEquals(pointCapletVol.Sensitivities.get(0), pointCapletVolExp); assertEquals(pointFloorletVol.Sensitivities.get(0), pointFloorletVolExp); }
public virtual void test_futuresConvexityFactorAdjoint() { HullWhiteOneFactorPiecewiseConstantParametersProvider provider = HullWhiteOneFactorPiecewiseConstantParametersProvider.of(PARAMETERS, ACT_360, DATE_TIME); LocalDate data1 = LocalDate.of(2015, 5, 14); LocalDate data2 = LocalDate.of(2015, 5, 20); LocalDate data3 = LocalDate.of(2015, 8, 20); ValueDerivatives computed = provider.futuresConvexityFactorAdjoint(data1, data2, data3); ValueDerivatives expected = HullWhiteOneFactorPiecewiseConstantInterestRateModel.DEFAULT.futuresConvexityFactorAdjoint(PARAMETERS, ACT_360.relativeYearFraction(VAL_DATE, data1), ACT_360.relativeYearFraction(VAL_DATE, data2), ACT_360.relativeYearFraction(VAL_DATE, data3)); assertEquals(computed, expected); }
//------------------------------------------------------------------------- public virtual void coverage() { DeformedSurface test1 = DeformedSurface.of(METADATA, SURFACE_ORG, FUNCTION); coverImmutableBean(test1); Surface surface1 = InterpolatedNodalSurface.of(DefaultSurfaceMetadata.of("TestSurface1"), XVALUES, YVALUES, ZVALUES, INTERPOLATOR); DeformedSurface test2 = DeformedSurface.of(DefaultSurfaceMetadata.of("DeformedTestSurface1"), surface1, (DoublesPair x) => { return(ValueDerivatives.of(surface1.zValue(x), surface1.zValueParameterSensitivity(x).Sensitivity)); }); coverBeanEquals(test1, test2); }
public virtual void implied_volatility_adjoint() { double shiftFd = 1.0E-6; for (int i = 0; i < N; i++) { double impliedVol = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, STRIKES[i], T, SIGMA_BLACK[i]); ValueDerivatives impliedVolAdj = NormalFormulaRepository.impliedVolatilityFromBlackApproximatedAdjoint(FORWARD, STRIKES[i], T, SIGMA_BLACK[i]); assertEquals(impliedVolAdj.Value, impliedVol, TOLERANCE_VOL); double impliedVolP = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, STRIKES[i], T, SIGMA_BLACK[i] + shiftFd); double impliedVolM = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, STRIKES[i], T, SIGMA_BLACK[i] - shiftFd); double derivativeApproximated = (impliedVolP - impliedVolM) / (2 * shiftFd); assertEquals(impliedVolAdj.Derivatives.size(), 1); assertEquals(impliedVolAdj.getDerivative(0), derivativeApproximated, TOLERANCE_VOL); } }
public virtual void test_zValue() { double tol = 1.0e-14; double x = 2.5; double y = 1.44; DeformedSurface test = DeformedSurface.of(METADATA, SURFACE_ORG, FUNCTION); double computedValue1 = test.zValue(x, y); double computedValue2 = test.zValue(DoublesPair.of(x, y)); UnitParameterSensitivity computedSensi1 = test.zValueParameterSensitivity(x, y); UnitParameterSensitivity computedSensi2 = test.zValueParameterSensitivity(DoublesPair.of(x, y)); ValueDerivatives expected = FUNCTION.apply(DoublesPair.of(x, y)); assertEquals(computedValue1, expected.Value); assertEquals(computedValue2, expected.Value); assertTrue(DoubleArrayMath.fuzzyEquals(computedSensi1.Sensitivity.toArray(), expected.Derivatives.toArray(), tol)); assertTrue(DoubleArrayMath.fuzzyEquals(computedSensi2.Sensitivity.toArray(), expected.Derivatives.toArray(), tol)); }
/// <summary> /// Tests the strikes computations. /// </summary> public virtual void strike() { double[] strike = SMILE.strike(FORWARD).toArrayUnsafe(); DoubleArray volatility = SMILE.Volatility; int nbDelta = DELTA.size(); for (int loopdelta = 0; loopdelta < nbDelta; loopdelta++) { ValueDerivatives dPut = BlackFormulaRepository.priceAdjoint(FORWARD, strike[loopdelta], TIME_TO_EXPIRY, volatility.get(loopdelta), false); assertEquals(-DELTA.get(loopdelta), dPut.getDerivative(0), 1e-8, "Strike: Put " + loopdelta); ValueDerivatives dCall = BlackFormulaRepository.priceAdjoint(FORWARD, strike[2 * nbDelta - loopdelta], TIME_TO_EXPIRY, volatility.get(2 * nbDelta - loopdelta), true); assertEquals(DELTA.get(loopdelta), dCall.getDerivative(0), 1e-8, "Strike: Call " + loopdelta); } ValueDerivatives dPut = BlackFormulaRepository.priceAdjoint(FORWARD, strike[nbDelta], TIME_TO_EXPIRY, volatility.get(nbDelta), false); ValueDerivatives dCall = BlackFormulaRepository.priceAdjoint(FORWARD, strike[nbDelta], TIME_TO_EXPIRY, volatility.get(nbDelta), true); assertEquals(0.0, dCall.getDerivative(0) + dPut.getDerivative(0), 1e-8, "Strike: ATM"); }
public virtual void test_volatility() { SabrParametersIborCapletFloorletVolatilities prov = SabrParametersIborCapletFloorletVolatilities.of(NAME, EUR_EURIBOR_3M, DATE_TIME, PARAM); for (int i = 0; i < NB_TEST; i++) { for (int j = 0; j < NB_STRIKE; ++j) { double expiryTime = prov.relativeTime(TEST_OPTION_EXPIRY[i]); double volExpected = PARAM.volatility(expiryTime, TEST_STRIKE[j], TEST_FORWARD); double volComputed = prov.volatility(TEST_OPTION_EXPIRY[i], TEST_STRIKE[j], TEST_FORWARD); assertEquals(volComputed, volExpected, TOLERANCE_VOL); ValueDerivatives volAdjExpected = PARAM.volatilityAdjoint(expiryTime, TEST_STRIKE[j], TEST_FORWARD); ValueDerivatives volAdjComputed = prov.volatilityAdjoint(expiryTime, TEST_STRIKE[j], TEST_FORWARD); assertEquals(volAdjComputed.Value, volExpected, TOLERANCE_VOL); assertTrue(DoubleArrayMath.fuzzyEquals(volAdjComputed.Derivatives.toArray(), volAdjExpected.Derivatives.toArray(), TOLERANCE_VOL)); } } }
//------------------------------------------------------------------------- /// <summary> /// Evaluates the function and its first derivative. /// <para> /// The dimension of {@code PiecewisePolynomialResult} must be 1. /// /// </para> /// </summary> /// <param name="pp"> the PiecewisePolynomialResult </param> /// <param name="xKey"> the key </param> /// <returns> the value and derivative </returns> public virtual ValueDerivatives evaluateAndDifferentiate(PiecewisePolynomialResult pp, double xKey) { ArgChecker.notNull(pp, "null pp"); ArgChecker.isFalse(double.IsNaN(xKey), "xKey containing NaN"); ArgChecker.isFalse(double.IsInfinity(xKey), "xKey containing Infinity"); if (pp.Dimensions > 1) { throw new System.NotSupportedException(); } DoubleArray knots = pp.Knots; int nKnots = knots.size(); int interval = FunctionUtils.getLowerBoundIndex(knots, xKey); if (interval == nKnots - 1) { interval--; // there is 1 less interval that knots } double s = xKey - knots.get(interval); DoubleArray coefs = pp.CoefMatrix.row(interval); int nCoefs = coefs.size(); double resValue = coefs.get(0); double resDeriv = coefs.get(0) * (nCoefs - 1); for (int i = 1; i < nCoefs - 1; i++) { resValue *= s; resValue += coefs.get(i); resDeriv *= s; resDeriv += coefs.get(i) * (nCoefs - i - 1); ArgChecker.isFalse(double.IsInfinity(resValue), "Too large input"); ArgChecker.isFalse(double.IsNaN(resValue), "Too large input"); } resValue *= s; resValue += coefs.get(nCoefs - 1); return(ValueDerivatives.of(resValue, DoubleArray.of(resDeriv))); }
public virtual void flatVolPriceTest() { double tol = 2.0e-2; double constantVol = 0.15; double spot = 100d; double maxTime = 1d; int nSteps = 9; ConstantSurface impliedVolSurface = ConstantSurface.of("impliedVol", constantVol); System.Func <double, double> zeroRate = (double?x) => { return(0d); }; System.Func <DoublesPair, ValueDerivatives> func = (DoublesPair x) => { double price = BlackFormulaRepository.price(spot, x.Second, x.First, constantVol, true); return(ValueDerivatives.of(price, DoubleArray.EMPTY)); }; DeformedSurface priceSurface = DeformedSurface.of(DefaultSurfaceMetadata.of("price"), impliedVolSurface, func); ImpliedTrinomialTreeLocalVolatilityCalculator calc = new ImpliedTrinomialTreeLocalVolatilityCalculator(nSteps, maxTime, INTERP_TIMESQ_LINEAR); InterpolatedNodalSurface localVolSurface = calc.localVolatilityFromPrice(priceSurface, spot, zeroRate, zeroRate); assertEquals(localVolSurface.ZValues.Where(d => !DoubleMath.fuzzyEquals(d, constantVol, tol)).Count(), 0); }