@Override public Double apply(Double x) { double[] kD = kpkpp(x); // Implementation note: kD[0] contains the first derivative of k; kD[1] the second derivative of k. double xShifted = Math.max(x + shift, 0d); // handle tiny but negative number DoubleArray priceDerivativeSabr = getSabrExtrapolation().priceAdjointSabr(xShifted, putCall).getDerivatives(); return priceDerivativeSabr.get(i) * (factor * (kD[1] * (x - strike) + 2d * kD[0])); } };
@Override public UnitParameterSensitivity zValueParameterSensitivity(double x, double y) { return getMetadata().getParameterMetadata().isPresent() ? UnitParameterSensitivity.of( getMetadata().getSurfaceName(), getMetadata().getParameterMetadata().get(), deformationFunction.apply(DoublesPair.of(x, y)).getDerivatives()) : UnitParameterSensitivity.of( getMetadata().getSurfaceName(), deformationFunction.apply(DoublesPair.of(x, y)).getDerivatives()); }
private double[] toArray(ValueDerivatives valueDerivatives) { double[] derivatives = valueDerivatives.getDerivatives().toArray(); double[] res = new double[derivatives.length + 1]; res[0] = valueDerivatives.getValue(); System.arraycopy(derivatives, 0, res, 1, derivatives.length); return res; }
private double[] computesFittingParameters() { double[] param = new double[3]; // Implementation note: called a,b,c in the note. // Computes derivatives at cut-off. double[] vD = new double[6]; double[][] vD2 = new double[2][2]; volatilityK = sabrFunction.volatilityAdjoint2(forward, cutOffStrike, timeToExpiry, sabrData, vD, vD2); Pair<ValueDerivatives, double[][]> pa2 = BlackFormulaRepository.priceAdjoint2(forward, cutOffStrike, timeToExpiry, volatilityK, true); double[] bsD = pa2.getFirst().getDerivatives().toArrayUnsafe(); double[][] bsD2 = pa2.getSecond(); priceK[0] = pa2.getFirst().getValue(); priceK[1] = bsD[1] + bsD[3] * vD[1]; priceK[2] = bsD2[1][1] + bsD2[1][2] * vD[1] + (bsD2[2][1] + bsD2[2][2] * vD[1]) * vD[1] + bsD[3] * vD2[1][1]; if (Math.abs(priceK[0]) < SMALL_PRICE && Math.abs(priceK[1]) < SMALL_PRICE && Math.abs(priceK[2]) < SMALL_PRICE) { // Implementation note: If value and its derivatives is too small, then parameters are such that the extrapolated price is "very small". return new double[] {-100.0, 0, 0}; } Function<Double, Double> toSolveC = getCFunction(priceK, cutOffStrike, mu); BracketRoot bracketer = new BracketRoot(); double accuracy = 1.0E-5; RidderSingleRootFinder rootFinder = new RidderSingleRootFinder(accuracy); double[] range = bracketer.getBracketedPoints(toSolveC, -1.0, 1.0); param[2] = rootFinder.getRoot(toSolveC, range[0], range[1]); param[1] = -2 * param[2] / cutOffStrike - (priceK[1] / priceK[0] * cutOffStrike + mu) * cutOffStrike; param[0] = Math.log(priceK[0] / Math.pow(cutOffStrike, -mu)) - param[1] / cutOffStrike - param[2] / (cutOffStrike * cutOffStrike); return param; }
@Override public DoubleMatrix apply(DoubleArray x) { final T data = toSmileModelData(x); double[][] resAdj = new double[n][]; for (int i = 0; i < n; ++i) { DoubleArray deriv = model.volatilityAdjoint(forward, strikes.get(i), timeToExpiry, data).getDerivatives(); resAdj[i] = deriv.subArray(2).toArrayUnsafe(); } return DoubleMatrix.copyOf(resAdj); } };
/** * Computes the derivative of the conventional cash annuity with respect to the yield from a swap leg. * <p> * The computation is relevant only for standard swaps with constant notional and regular payments. * The swap leg must be a fixed leg. However, this is not checked internally. * * @param fixedLeg the fixed leg of the swap * @param yield the yield * @return the cash annuity */ public ValueDerivatives annuityCashDerivative(ResolvedSwapLeg fixedLeg, double yield) { int nbFixedPeriod = fixedLeg.getPaymentPeriods().size(); SwapPaymentPeriod paymentPeriod = fixedLeg.getPaymentPeriods().get(0); ArgChecker.isTrue(paymentPeriod instanceof RatePaymentPeriod, "payment period should be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod) paymentPeriod; int nbFixedPaymentYear = (int) Math.round(1d / ratePaymentPeriod.getDayCount().yearFraction(ratePaymentPeriod.getStartDate(), ratePaymentPeriod.getEndDate())); double notional = Math.abs(ratePaymentPeriod.getNotional()); ValueDerivatives annuityUnit = annuityCash1(nbFixedPaymentYear, nbFixedPeriod, yield); return ValueDerivatives.of(annuityUnit.getValue() * notional, annuityUnit.getDerivatives().multipliedBy(notional)); }
public void test_of() { ValueDerivatives test = ValueDerivatives.of(VALUE, DERIVATIVES); assertEquals(test.getValue(), VALUE, 0); assertEquals(test.getDerivatives(), DERIVATIVES); assertEquals(test.getDerivative(0), DERIVATIVES.get(0)); assertEquals(test.getDerivative(1), DERIVATIVES.get(1)); assertEquals(test.getDerivative(2), DERIVATIVES.get(2)); }
public void implied_volatility_from_normal_adjoint() { double shiftFd = 1.0E-6; for (int i = 0; i < N; i++) { double ivBlackComputed = BlackFormulaRepository .impliedVolatilityFromNormalApproximated(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i]); ValueDerivatives ivBlackAdj = BlackFormulaRepository .impliedVolatilityFromNormalApproximatedAdjoint(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i]); assertEquals(ivBlackComputed, ivBlackAdj.getValue(), TOLERANCE_1); assertEquals(1, ivBlackAdj.getDerivatives().size()); double ivBlackComputedP = BlackFormulaRepository .impliedVolatilityFromNormalApproximated(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i] + shiftFd); double ivBlackComputedM = BlackFormulaRepository .impliedVolatilityFromNormalApproximated(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i] - shiftFd); double derivativeApproximated = (ivBlackComputedP - ivBlackComputedM) / (2 * shiftFd); assertEquals(derivativeApproximated, ivBlackAdj.getDerivative(0), TOLERANCE_VOL_DELTA); } }
public 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.getValue(), volExpected, TOLERANCE_VOL); assertTrue(DoubleArrayMath.fuzzyEquals( volAdjComputed.getDerivatives().toArray(), volAdjExpected.getDerivatives().toArray(), TOLERANCE_VOL)); } } }
@Test public void derivatives() { // AD v Finite Difference ScalarFieldFirstOrderDifferentiator differentiator = new ScalarFieldFirstOrderDifferentiator(FiniteDifferenceType.CENTRAL, 1.0E-5); for (int i = 0; i < N; i++) { Function<DoubleArray, Double> function = new Function<DoubleArray, Double>() { @Override public Double apply(DoubleArray x) { SsviFormulaData data = SsviFormulaData.of(x.get(3), x.get(4), x.get(5)); return SSVI_FUNCTION.volatility(x.get(0), x.get(1), x.get(2), data); } }; Function<DoubleArray, DoubleArray> d = differentiator.differentiate(function); DoubleArray fd = d.apply(DoubleArray.of(FORWARD, STRIKES[i], TIME_EXP, VOL_ATM, RHO, ETA)); ValueDerivatives ad = SSVI_FUNCTION.volatilityAdjoint(FORWARD, STRIKES[i], TIME_EXP, DATA); for (int j = 0; j < 6; j++) { assertEquals(fd.get(j), ad.getDerivatives().get(j), TOLERANCE_AD); } } }
public 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.getValue(), 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.getDerivatives().size(), 1); assertEquals(impliedVolAdj.getDerivative(0), derivativeApproximated, TOLERANCE_VOL); } }
public 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.getValue()); assertEquals(computedValue2, expected.getValue()); assertTrue(DoubleArrayMath.fuzzyEquals( computedSensi1.getSensitivity().toArray(), expected.getDerivatives().toArray(), tol)); assertTrue(DoubleArrayMath.fuzzyEquals( computedSensi2.getSensitivity().toArray(), expected.getDerivatives().toArray(), tol)); }
/** * Consistency with evaluate and differentiate. */ @Test public void evaluateAndDifferentiateTest() { double[][][] coefsMatrix = new double[][][] { { {1., -3., 3., -1 }, {1., 0., 0., 0. }, {1., 3., 3., 1. }, }, { {0., 5., -20., 20 }, {0., 5., -10., 5 }, {0., 5., 0., 0. } } }; double[][] xKeys = new double[][] { {-2, 1, 2, 2.5 }, {1.5, 7. / 3., 29. / 7., 5. } }; int dim = 2; int nCoefs = 4; int keyLength = xKeys[0].length; PiecewisePolynomialResult[] pp = new PiecewisePolynomialResult[dim]; for (int i = 0; i < dim; ++i) { pp[i] = new PiecewisePolynomialResult(X_VALUES, DoubleMatrix.ofUnsafe(coefsMatrix[i]), nCoefs, 1); } PiecewisePolynomialFunction1D function = new PiecewisePolynomialFunction1D(); for (int i = 0; i < dim; ++i) { for (int j = 0; j < keyLength; ++j) { ValueDerivatives computed = function.evaluateAndDifferentiate(pp[i], xKeys[i][j]); double value = function.evaluate(pp[i], xKeys[i][j]).get(0); double deriv = function.differentiate(pp[i], xKeys[i][j]).get(0); assertEquals(computed.getValue(), value, EPS); assertEquals(computed.getDerivatives().size(), 1); assertEquals(computed.getDerivative(0), deriv, EPS); } } }
public void negativeRates() { double shift = 0.05; Curve surface = ConstantCurve.of("shfit", shift); SabrParameters params = SabrParameters.of(ALPHA_CURVE, BETA_CURVE, RHO_CURVE, NU_CURVE, surface, FORMULA); double expiry = 2.0; assertEquals(params.alpha(expiry), ALPHA_CURVE.yValue(expiry)); assertEquals(params.beta(expiry), BETA_CURVE.yValue(expiry)); assertEquals(params.rho(expiry), RHO_CURVE.yValue(expiry)); assertEquals(params.nu(expiry), NU_CURVE.yValue(expiry)); double strike = -0.02; double forward = 0.015; double alpha = ALPHA_CURVE.yValue(expiry); double beta = BETA_CURVE.yValue(expiry); double rho = RHO_CURVE.yValue(expiry); double nu = NU_CURVE.yValue(expiry); assertEquals(params.volatility(expiry, strike, forward), FORMULA.volatility(forward + shift, strike + shift, expiry, alpha, beta, rho, nu)); double[] adjCmp = params.volatilityAdjoint(expiry, strike, forward).getDerivatives().toArray(); double[] adjExp = FORMULA.volatilityAdjoint( forward + shift, strike + shift, expiry, alpha, beta, rho, nu).getDerivatives().toArray(); for (int i = 0; i < 4; ++i) { assertEquals(adjCmp[i], adjExp[i]); } }
public void negativeRates() { double shift = 0.05; Surface surface = ConstantSurface.of("shfit", shift); SabrInterestRateParameters params = SabrInterestRateParameters.of(ALPHA_SURFACE, BETA_SURFACE, RHO_SURFACE, NU_SURFACE, surface, FORMULA); double expiry = 2.0; double tenor = 3.0; assertEquals(params.alpha(expiry, tenor), ALPHA_SURFACE.zValue(expiry, tenor)); assertEquals(params.beta(expiry, tenor), BETA_SURFACE.zValue(expiry, tenor)); assertEquals(params.rho(expiry, tenor), RHO_SURFACE.zValue(expiry, tenor)); assertEquals(params.nu(expiry, tenor), NU_SURFACE.zValue(expiry, tenor)); double strike = -0.02; double forward = 0.015; double alpha = ALPHA_SURFACE.zValue(expiry, tenor); double beta = BETA_SURFACE.zValue(expiry, tenor); double rho = RHO_SURFACE.zValue(expiry, tenor); double nu = NU_SURFACE.zValue(expiry, tenor); assertEquals(params.volatility(expiry, tenor, strike, forward), FORMULA.volatility(forward + shift, strike + shift, expiry, alpha, beta, rho, nu)); double[] adjCmp = params.volatilityAdjoint(expiry, tenor, strike, forward).getDerivatives().toArray(); double[] adjExp = FORMULA.volatilityAdjoint( forward + shift, strike + shift, expiry, alpha, beta, rho, nu).getDerivatives().toArray(); for (int i = 0; i < 4; ++i) { assertEquals(adjCmp[i], adjExp[i]); } }
/** * Calculates the price sensitivity to piecewise constant volatility parameters of the Hull-White model. * * @param future the future * @param ratesProvider the rates provider * @param hwProvider the Hull-White model parameter provider * @return the price parameter sensitivity of the product */ public DoubleArray priceSensitivityModelParamsHullWhite( ResolvedIborFuture future, RatesProvider ratesProvider, HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider) { IborIndexObservation obs = future.getIborRate().getObservation(); double forward = ratesProvider.iborIndexRates(future.getIndex()).rate(obs); LocalDate fixingStartDate = obs.getEffectiveDate(); LocalDate fixingEndDate = obs.getMaturityDate(); double fixingYearFraction = obs.getYearFraction(); DoubleArray convexityDeriv = hwProvider.futuresConvexityFactorAdjoint( future.getLastTradeDate(), fixingStartDate, fixingEndDate).getDerivatives(); convexityDeriv = convexityDeriv.multipliedBy(-forward - 1d / fixingYearFraction); return convexityDeriv; } }
public void swapRateDa() { double shift = 1.0E-8; double x = 0.0; ValueDerivatives computed = MODEL.swapRateDaf1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double swapRateComputed = computed.getValue(); double[] dafComputed = computed.getDerivatives().toArray(); double swapRateExpected = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); assertEquals(swapRateComputed, swapRateExpected, TOLERANCE_RATE); double[] dafExpected = new double[ALPHA_FIXED.size()]; for (int loopcf = 0; loopcf < ALPHA_FIXED.size(); loopcf++) { double[] afBumped = ALPHA_FIXED.toArray(); afBumped[loopcf] += shift; double swapRatePlus = MODEL.swapRate(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR); afBumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRate(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR); dafExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } assertTrue(DoubleArrayMath.fuzzyEquals(dafExpected, dafComputed, TOLERANCE_RATE_DELTA)); double[] daiExpected = new double[DCF_IBOR.size()]; for (int loopcf = 0; loopcf < DCF_IBOR.size(); loopcf++) { double[] aiBumped = ALPHA_IBOR.toArray(); aiBumped[loopcf] += shift; double swapRatePlus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped)); aiBumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped)); daiExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } double[] daiComputed = MODEL.swapRateDai1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR).getDerivatives().toArray(); assertTrue(DoubleArrayMath.fuzzyEquals(daiExpected, daiComputed, TOLERANCE_RATE_DELTA)); }
ValueDerivatives computed = MODEL.swapRateDdcff1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double swapRateComputed = computed.getValue(); double[] ddcffComputed = computed.getDerivatives().toArray(); double swapRateExpected = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); assertEquals(swapRateComputed, swapRateExpected, TOLERANCE_RATE); .getDerivatives().toArray(); assertTrue(DoubleArrayMath.fuzzyEquals(ddcfiExpected, ddcfiComputed, TOLERANCE_RATE_DELTA));
public void test_presentValueSensitivityModelParamsSabr() { PointSensitivities sensiRec = SWAPTION_PRICER.presentValueSensitivityModelParamsSabr(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS).build(); PointSensitivities sensiPay = SWAPTION_PRICER.presentValueSensitivityModelParamsSabr(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS).build(); double forward = SWAP_PRICER.parRate(RSWAP_REC, RATE_PROVIDER); double pvbp = SWAP_PRICER.getLegPricer().pvbp(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), RATE_PROVIDER); double volatility = VOLS.volatility(SWAPTION_REC_LONG.getExpiry(), TENOR_YEAR, RATE, forward); double maturity = VOLS.relativeTime(SWAPTION_REC_LONG.getExpiry()); double[] volSensi = VOLS.getParameters() .volatilityAdjoint(maturity, TENOR_YEAR, RATE, forward).getDerivatives().toArray(); double vegaRec = pvbp * BlackFormulaRepository.vega(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, maturity, volatility); double vegaPay = -pvbp * BlackFormulaRepository.vega(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, maturity, volatility); assertSensitivity(sensiRec, SabrParameterType.ALPHA, vegaRec * volSensi[2], TOL); assertSensitivity(sensiRec, SabrParameterType.BETA, vegaRec * volSensi[3], TOL); assertSensitivity(sensiRec, SabrParameterType.RHO, vegaRec * volSensi[4], TOL); assertSensitivity(sensiRec, SabrParameterType.NU, vegaRec * volSensi[5], TOL); assertSensitivity(sensiPay, SabrParameterType.ALPHA, vegaPay * volSensi[2], TOL); assertSensitivity(sensiPay, SabrParameterType.BETA, vegaPay * volSensi[3], TOL); assertSensitivity(sensiPay, SabrParameterType.RHO, vegaPay * volSensi[4], TOL); assertSensitivity(sensiPay, SabrParameterType.NU, vegaPay * volSensi[5], TOL); }
public void test_presentValueSensitivityModelParamsSabr() { PointSensitivities sensiRec = PRICER.presentValueSensitivityModelParamsSabr(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS).build(); PointSensitivities sensiPay = PRICER.presentValueSensitivityModelParamsSabr(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS).build(); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RFIXED_LEG_REC, forward); double expiry = VOLS.relativeTime(MATURITY); double volatility = VOLS.volatility(SWAPTION_REC_LONG.getExpiry(), TENOR_YEAR, RATE, forward); double df = RATE_PROVIDER.discountFactor(EUR, SETTLE); double[] volSensi = VOLS.getParameters().volatilityAdjoint(expiry, TENOR_YEAR, RATE, forward).getDerivatives().toArray(); double vegaRec = df * annuityCash * BlackFormulaRepository.vega(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, expiry, volatility); double vegaPay = -df * annuityCash * BlackFormulaRepository.vega(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, expiry, volatility); assertSensitivity(sensiRec, SabrParameterType.ALPHA, vegaRec * volSensi[2]); assertSensitivity(sensiRec, SabrParameterType.BETA, vegaRec * volSensi[3]); assertSensitivity(sensiRec, SabrParameterType.RHO, vegaRec * volSensi[4]); assertSensitivity(sensiRec, SabrParameterType.NU, vegaRec * volSensi[5]); assertSensitivity(sensiPay, SabrParameterType.ALPHA, vegaPay * volSensi[2]); assertSensitivity(sensiPay, SabrParameterType.BETA, vegaPay * volSensi[3]); assertSensitivity(sensiPay, SabrParameterType.RHO, vegaPay * volSensi[4]); assertSensitivity(sensiPay, SabrParameterType.NU, vegaPay * volSensi[5]); }