//------------------------------------------------------------------------- public virtual void test_presentValueSensitivityHullWhiteParameter() { DoubleArray computedRec = PRICER.presentValueSensitivityModelParamsHullWhite(SWAPTION_REC_LONG, RATE_PROVIDER, HW_PROVIDER); DoubleArray computedPay = PRICER.presentValueSensitivityModelParamsHullWhite(SWAPTION_PAY_SHORT, RATE_PROVIDER, HW_PROVIDER); DoubleArray vols = HW_PROVIDER.Parameters.Volatility; int size = vols.size(); double[] expectedRec = new double[size]; double[] expectedPay = new double[size]; for (int i = 0; i < size; ++i) { double[] volsUp = vols.toArray(); double[] volsDw = vols.toArray(); volsUp[i] += FD_TOL; volsDw[i] -= FD_TOL; HullWhiteOneFactorPiecewiseConstantParameters paramsUp = HullWhiteOneFactorPiecewiseConstantParameters.of(HW_PROVIDER.Parameters.MeanReversion, DoubleArray.copyOf(volsUp), HW_PROVIDER.Parameters.VolatilityTime.subArray(1, size)); HullWhiteOneFactorPiecewiseConstantParameters paramsDw = HullWhiteOneFactorPiecewiseConstantParameters.of(HW_PROVIDER.Parameters.MeanReversion, DoubleArray.copyOf(volsDw), HW_PROVIDER.Parameters.VolatilityTime.subArray(1, size)); HullWhiteOneFactorPiecewiseConstantParametersProvider provUp = HullWhiteOneFactorPiecewiseConstantParametersProvider.of(paramsUp, HW_PROVIDER.DayCount, HW_PROVIDER.ValuationDateTime); HullWhiteOneFactorPiecewiseConstantParametersProvider provDw = HullWhiteOneFactorPiecewiseConstantParametersProvider.of(paramsDw, HW_PROVIDER.DayCount, HW_PROVIDER.ValuationDateTime); expectedRec[i] = 0.5 * (PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, provUp).Amount - PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, provDw).Amount) / FD_TOL; expectedPay[i] = 0.5 * (PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, provUp).Amount - PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, provDw).Amount) / FD_TOL; } assertTrue(DoubleArrayMath.fuzzyEquals(computedRec.toArray(), expectedRec, NOTIONAL * FD_TOL)); assertTrue(DoubleArrayMath.fuzzyEquals(computedPay.toArray(), expectedPay, NOTIONAL * FD_TOL)); }
// handling short time to expiry private double computeKappa(HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider, double[] discountedCashFlow, double[] alpha, double omega) { double kappa = 0d; if (DoubleArrayMath.fuzzyEqualsZero(alpha, SMALL)) { // threshold coherent to rootfinder in kappa computation double totalPv = DoubleArrayMath.sum(discountedCashFlow); kappa = totalPv * omega > 0d ? double.PositiveInfinity : double.NegativeInfinity; } else { kappa = hwProvider.Model.kappa(DoubleArray.ofUnsafe(discountedCashFlow), DoubleArray.ofUnsafe(alpha)); } return(kappa); }
/// <summary> /// Calculates the present value of the swaption product. /// <para> /// The result is expressed using the currency of the swapion. /// /// </para> /// </summary> /// <param name="swaption"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="hwProvider"> the Hull-White model parameter provider </param> /// <returns> the present value </returns> public virtual CurrencyAmount presentValue(ResolvedSwaption swaption, RatesProvider ratesProvider, HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider) { validate(swaption, ratesProvider, hwProvider); ResolvedSwap swap = swaption.Underlying; LocalDate expiryDate = swaption.ExpiryDate; if (expiryDate.isBefore(ratesProvider.ValuationDate)) { // Option has expired already return(CurrencyAmount.of(swap.Legs.get(0).Currency, 0d)); } ResolvedSwapLeg cashFlowEquiv = CashFlowEquivalentCalculator.cashFlowEquivalentSwap(swap, ratesProvider); int nPayments = cashFlowEquiv.PaymentEvents.size(); double[] alpha = new double[nPayments]; double[] discountedCashFlow = new double[nPayments]; for (int loopcf = 0; loopcf < nPayments; loopcf++) { NotionalExchange payment = (NotionalExchange)cashFlowEquiv.PaymentEvents.get(loopcf); LocalDate maturityDate = payment.PaymentDate; alpha[loopcf] = hwProvider.alpha(ratesProvider.ValuationDate, expiryDate, expiryDate, maturityDate); discountedCashFlow[loopcf] = paymentPricer.presentValueAmount(payment.Payment, ratesProvider); } double omega = (swap.getLegs(SwapLegType.FIXED).get(0).PayReceive.Pay ? -1d : 1d); double kappa = computeKappa(hwProvider, discountedCashFlow, alpha, omega); double pv = 0.0; for (int loopcf = 0; loopcf < nPayments; loopcf++) { pv += discountedCashFlow[loopcf] * NORMAL.getCDF(omega * (kappa + alpha[loopcf])); } return(CurrencyAmount.of(cashFlowEquiv.Currency, pv * (swaption.LongShort.Long ? 1d : -1d))); }
//------------------------------------------------------------------------- // validate that the rates and volatilities providers are coherent private void validate(ResolvedSwaption swaption, RatesProvider ratesProvider, HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider) { ArgChecker.isTrue(hwProvider.ValuationDateTime.toLocalDate().Equals(ratesProvider.ValuationDate), "Hull-White model data and rate data should be for the same date"); ArgChecker.isFalse(swaption.Underlying.CrossCurrency, "underlying swap should be single currency"); ArgChecker.isTrue(swaption.SwaptionSettlement.SettlementType.Equals(SettlementType.PHYSICAL), "swaption should be physical settlement"); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity to piecewise constant volatility parameters of the Hull-White model. /// </summary> /// <param name="swaption"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="hwProvider"> the Hull-White model parameter provider </param> /// <returns> the present value Hull-White model parameter sensitivity of the swaption product </returns> public virtual DoubleArray presentValueSensitivityModelParamsHullWhite(ResolvedSwaption swaption, RatesProvider ratesProvider, HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider) { validate(swaption, ratesProvider, hwProvider); ResolvedSwap swap = swaption.Underlying; LocalDate expiryDate = swaption.ExpiryDate; if (expiryDate.isBefore(ratesProvider.ValuationDate)) { // Option has expired already return(DoubleArray.EMPTY); } ResolvedSwapLeg cashFlowEquiv = CashFlowEquivalentCalculator.cashFlowEquivalentSwap(swap, ratesProvider); int nPayments = cashFlowEquiv.PaymentEvents.size(); double[] alpha = new double[nPayments]; double[][] alphaAdjoint = new double[nPayments][]; double[] discountedCashFlow = new double[nPayments]; for (int loopcf = 0; loopcf < nPayments; loopcf++) { NotionalExchange payment = (NotionalExchange)cashFlowEquiv.PaymentEvents.get(loopcf); ValueDerivatives valueDeriv = hwProvider.alphaAdjoint(ratesProvider.ValuationDate, expiryDate, expiryDate, payment.PaymentDate); alpha[loopcf] = valueDeriv.Value; alphaAdjoint[loopcf] = valueDeriv.Derivatives.toArray(); discountedCashFlow[loopcf] = paymentPricer.presentValueAmount(payment.Payment, ratesProvider); } double omega = (swap.getLegs(SwapLegType.FIXED).get(0).PayReceive.Pay ? -1d : 1d); double kappa = computeKappa(hwProvider, discountedCashFlow, alpha, omega); int nParams = alphaAdjoint[0].Length; if (Math.Abs(kappa) > 1d / SMALL) { // decays exponentially return(DoubleArray.filled(nParams)); } double[] pvSensi = new double[nParams]; double sign = (swaption.LongShort.Long ? 1d : -1d); for (int i = 0; i < nParams; ++i) { for (int loopcf = 0; loopcf < nPayments; loopcf++) { pvSensi[i] += sign * discountedCashFlow[loopcf] * NORMAL.getPDF(omega * (kappa + alpha[loopcf])) * omega * alphaAdjoint[loopcf][i]; } } return(DoubleArray.ofUnsafe(pvSensi)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="swaption"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="hwProvider"> the Hull-White model parameter provider </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRates(ResolvedSwaption swaption, RatesProvider ratesProvider, HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider) { validate(swaption, ratesProvider, hwProvider); ResolvedSwap swap = swaption.Underlying; LocalDate expiryDate = swaption.ExpiryDate; if (expiryDate.isBefore(ratesProvider.ValuationDate)) { // Option has expired already return(PointSensitivityBuilder.none()); } ImmutableMap <Payment, PointSensitivityBuilder> cashFlowEquivSensi = CashFlowEquivalentCalculator.cashFlowEquivalentAndSensitivitySwap(swap, ratesProvider); ImmutableList <Payment> list = cashFlowEquivSensi.Keys.asList(); ImmutableList <PointSensitivityBuilder> listSensi = cashFlowEquivSensi.values().asList(); int nPayments = list.size(); double[] alpha = new double[nPayments]; double[] discountedCashFlow = new double[nPayments]; for (int loopcf = 0; loopcf < nPayments; loopcf++) { Payment payment = list.get(loopcf); alpha[loopcf] = hwProvider.alpha(ratesProvider.ValuationDate, expiryDate, expiryDate, payment.Date); discountedCashFlow[loopcf] = paymentPricer.presentValueAmount(payment, ratesProvider); } double omega = (swap.getLegs(SwapLegType.FIXED).get(0).PayReceive.Pay ? -1d : 1d); double kappa = computeKappa(hwProvider, discountedCashFlow, alpha, omega); PointSensitivityBuilder point = PointSensitivityBuilder.none(); for (int loopcf = 0; loopcf < nPayments; loopcf++) { Payment payment = list.get(loopcf); double cdf = NORMAL.getCDF(omega * (kappa + alpha[loopcf])); point = point.combinedWith(paymentPricer.presentValueSensitivity(payment, ratesProvider).multipliedBy(cdf)); if (!listSensi.get(loopcf).Equals(PointSensitivityBuilder.none())) { point = point.combinedWith(listSensi.get(loopcf).multipliedBy(cdf * ratesProvider.discountFactor(payment.Currency, payment.Date))); } } return(swaption.LongShort.Long ? point : point.multipliedBy(-1d)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the currency exposure of the swaption product. /// </summary> /// <param name="swaption"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="hwProvider"> the Hull-White model parameter provider </param> /// <returns> the currency exposure </returns> public virtual MultiCurrencyAmount currencyExposure(ResolvedSwaption swaption, RatesProvider ratesProvider, HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider) { return(MultiCurrencyAmount.of(presentValue(swaption, ratesProvider, hwProvider))); }