public virtual void present_value_delta_payer_receiver_parity() { CurrencyAmount pvDeltaLongPay = PRICER_SWAPTION_BLACK.presentValueDelta(SWAPTION_LONG_PAY, MULTI_USD, BLACK_VOLS_USD_STD); CurrencyAmount pvDeltaShortRec = PRICER_SWAPTION_BLACK.presentValueDelta(SWAPTION_SHORT_REC, MULTI_USD, BLACK_VOLS_USD_STD); double pvbp = PRICER_SWAP.LegPricer.pvbp(RSWAP_PAY.getLegs(SwapLegType.FIXED).get(0), MULTI_USD); assertEquals(pvDeltaLongPay.Amount + pvDeltaShortRec.Amount, Math.Abs(pvbp), TOLERANCE_PV); }
/// <summary> /// Computes cash flow equivalent of swap. /// <para> /// The swap should be a fix-for-Ibor swap without compounding, and its swap legs /// should not involve {@code PaymentEvent}. /// </para> /// <para> /// The return type is {@code ResolvedSwapLeg} in which individual payments are /// represented in terms of {@code NotionalExchange}. /// /// </para> /// </summary> /// <param name="swap"> the swap product </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the cash flow equivalent </returns> public static ResolvedSwapLeg cashFlowEquivalentSwap(ResolvedSwap swap, RatesProvider ratesProvider) { validateSwap(swap); ResolvedSwapLeg cfFixed = cashFlowEquivalentFixedLeg(swap.getLegs(SwapLegType.FIXED).get(0), ratesProvider); ResolvedSwapLeg cfIbor = cashFlowEquivalentIborLeg(swap.getLegs(SwapLegType.IBOR).get(0), ratesProvider); ResolvedSwapLeg leg = ResolvedSwapLeg.builder().paymentEvents(Stream.concat(cfFixed.PaymentEvents.stream(), cfIbor.PaymentEvents.stream()).collect(Collectors.toList())).payReceive(PayReceive.RECEIVE).type(SwapLegType.OTHER).build(); return(leg); }
//------------------------------------------------------------------------- public virtual void present_value_formula() { double forward = PRICER_SWAP.parRate(RSWAP_REC, MULTI_USD); double pvbp = PRICER_SWAP.LegPricer.pvbp(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), MULTI_USD); double volatility = BLACK_VOLS_USD_STD.volatility(SWAPTION_LONG_REC.Expiry, SWAP_TENOR_YEAR, STRIKE, forward); double expiry = BLACK_VOLS_USD_STD.relativeTime(SWAPTION_LONG_REC.Expiry); double pvExpected = Math.Abs(pvbp) * BlackFormulaRepository.price(forward, STRIKE, expiry, volatility, false); CurrencyAmount pvComputed = PRICER_SWAPTION_BLACK.presentValue(SWAPTION_LONG_REC, MULTI_USD, BLACK_VOLS_USD_STD); assertEquals(pvComputed.Currency, USD); assertEquals(pvComputed.Amount, pvExpected, TOLERANCE_PV); }
//------------------------------------------------------------------------- public virtual void test_presentValue() { CurrencyAmount computedRec = SWAPTION_PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount computedPay = SWAPTION_PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); double forward = SWAP_PRICER.parRate(RSWAP_REC, RATE_PROVIDER); double pvbp = SWAP_PRICER.LegPricer.pvbp(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), RATE_PROVIDER); double volatility = VOLS.volatility(SWAPTION_REC_LONG.Expiry, TENOR_YEAR, RATE, forward); double maturity = VOLS.relativeTime(SWAPTION_REC_LONG.Expiry); double expectedRec = pvbp * BlackFormulaRepository.price(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, maturity, volatility, false); double expectedPay = -pvbp *BlackFormulaRepository.price(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, maturity, volatility, true); assertEquals(computedRec.Currency, USD); assertEquals(computedRec.Amount, expectedRec, NOTIONAL * TOL); assertEquals(computedPay.Currency, USD); assertEquals(computedPay.Amount, expectedPay, NOTIONAL * TOL); }
/// <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))); }
public virtual void test_presentValue_parity() { CurrencyAmount pvRecLong = PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvRecShort = PRICER.presentValue(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); CurrencyAmount pvPayLong = PRICER.presentValue(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvPayShort = PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); assertEquals(pvRecLong.Amount, -pvRecShort.Amount, NOTIONAL * TOL); assertEquals(pvPayLong.Amount, -pvPayShort.Amount, NOTIONAL * TOL); double forward = SWAP_PRICER.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = SWAP_PRICER.LegPricer.annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double discount = RATE_PROVIDER.discountFactor(EUR, SETTLE); double expected = discount * annuityCash * (forward - RATE); assertEquals(pvPayLong.Amount - pvRecLong.Amount, expected, NOTIONAL * TOL); assertEquals(pvPayShort.Amount - pvRecShort.Amount, -expected, NOTIONAL * TOL); }
//------------------------------------------------------------------------- // checking that at least one leg is a fixed leg and returning the first one private ResolvedSwapLeg fixedLeg(ResolvedSwap swap) { IList <ResolvedSwapLeg> fixedLegs = swap.getLegs(SwapLegType.FIXED); if (fixedLegs.Count == 0) { throw new System.ArgumentException("Swap must contain a fixed leg"); } return(fixedLegs[0]); }
/// <summary> /// Checks that there is exactly one fixed leg and returns it. /// </summary> /// <param name="swap"> the swap </param> /// <returns> the fixed leg </returns> protected internal virtual ResolvedSwapLeg fixedLeg(ResolvedSwap swap) { ArgChecker.isFalse(swap.CrossCurrency, "Swap must be single currency"); // find fixed leg IList <ResolvedSwapLeg> fixedLegs = swap.getLegs(SwapLegType.FIXED); if (fixedLegs.Count == 0) { throw new System.ArgumentException("Swap must contain a fixed leg"); } return(fixedLegs[0]); }
//------------------------------------------------------------------------- /// <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)); }
//------------------------------------------------------------------------- public virtual void present_value_formula() { double forward = PRICER_SWAP.parRate(RSWAP_REC, MULTI_USD); double pvbp = PRICER_SWAP.LegPricer.pvbp(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), MULTI_USD); double volatility = NORMAL_VOLS_USD_STD.volatility(SWAPTION_LONG_REC.Expiry, SWAP_TENOR_YEAR, STRIKE, forward); NormalFunctionData normalData = NormalFunctionData.of(forward, Math.Abs(pvbp), volatility); double expiry = NORMAL_VOLS_USD_STD.relativeTime(SWAPTION_LONG_REC.Expiry); EuropeanVanillaOption option = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.PUT); double pvExpected = NORMAL.getPriceFunction(option).apply(normalData); CurrencyAmount pvComputed = PRICER_SWAPTION_NORMAL.presentValue(SWAPTION_LONG_REC, MULTI_USD, NORMAL_VOLS_USD_STD); assertEquals(pvComputed.Currency, USD); assertEquals(pvComputed.Amount, pvExpected, TOLERANCE_PV); }
//------------------------------------------------------------------------- public virtual void test_presentValue() { CurrencyAmount pvRecComputed = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvPayComputed = PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.LegPricer.annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double volatility = VOLS.volatility(SWAPTION_REC_LONG.Expiry, SWAP_TENOR_YEAR, STRIKE, forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); NormalFunctionData normalData = NormalFunctionData.of(forward, annuityCash * discount, volatility); double expiry = VOLS.relativeTime(SWAPTION_REC_LONG.Expiry); EuropeanVanillaOption optionRec = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.PUT); EuropeanVanillaOption optionPay = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.CALL); double pvRecExpected = NORMAL.getPriceFunction(optionRec).apply(normalData); double pvPayExpected = -NORMAL.getPriceFunction(optionPay).apply(normalData); assertEquals(pvRecComputed.Currency, USD); assertEquals(pvRecComputed.Amount, pvRecExpected, NOTIONAL * TOL); assertEquals(pvPayComputed.Currency, USD); assertEquals(pvPayComputed.Amount, pvPayExpected, NOTIONAL * TOL); }
//------------------------------------------------------------------------- /// <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)); }
public CmsIntegrantProvider(SabrExtrapolationReplicationCmsPeriodPricer outerInstance, CmsPeriod cmsPeriod, ResolvedSwap swap, SabrSwaptionVolatilities swaptionVolatilities, double forward, double strike, double timeToExpiry, double tenor, double cutOffStrike, double eta) { this.outerInstance = outerInstance; ResolvedSwapLeg fixedLeg = swap.getLegs(SwapLegType.FIXED).get(0); this.nbFixedPeriod = fixedLeg.PaymentPeriods.size(); this.nbFixedPaymentYear = (int)(long)Math.Round(1d / ((RatePaymentPeriod)fixedLeg.PaymentPeriods.get(0)).AccrualPeriods.get(0).YearFraction, MidpointRounding.AwayFromZero); this.tau = 1d / nbFixedPaymentYear; this.eta = eta; SabrFormulaData sabrPoint = SabrFormulaData.of(swaptionVolatilities.alpha(timeToExpiry, tenor), swaptionVolatilities.beta(timeToExpiry, tenor), swaptionVolatilities.rho(timeToExpiry, tenor), swaptionVolatilities.nu(timeToExpiry, tenor)); this.shift = swaptionVolatilities.shift(timeToExpiry, tenor); this.sabrExtrapolation = SabrExtrapolationRightFunction.of(forward + shift, timeToExpiry, sabrPoint, cutOffStrike + shift, outerInstance.mu); this.putCall = cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.FLOORLET) ? PutCall.PUT : PutCall.CALL; this.strike = strike; this.factor = g(forward) / h(forward); this.g0 = new double[4]; g0[0] = nbFixedPeriod * tau; g0[1] = -0.5 * nbFixedPeriod * (nbFixedPeriod + 1.0d) * tau * tau; g0[2] = -2.0d / 3.0d * g0[1] * (nbFixedPeriod + 2.0d) * tau; g0[3] = -3.0d / 4.0d * g0[2] * (nbFixedPeriod + 2.0d) * tau; }
//------------------------------------------------------------------------- private static void validateSwap(ResolvedSwap swap) { ArgChecker.isTrue(swap.Legs.size() == 2, "swap should have 2 legs"); ArgChecker.isTrue(swap.getLegs(SwapLegType.FIXED).size() == 1, "swap should have unique fixed leg"); ArgChecker.isTrue(swap.getLegs(SwapLegType.IBOR).size() == 1, "swap should have unique Ibor leg"); }
//------------------------------------------------------------------------- /// <summary> /// Computes cash flow equivalent and sensitivity of swap. /// <para> /// The swap should be a fix-for-Ibor swap without compounding, and its swap legs should not involve {@code PaymentEvent}. /// </para> /// <para> /// The return type is a map of {@code NotionalExchange} and {@code PointSensitivityBuilder}. /// /// </para> /// </summary> /// <param name="swap"> the swap product </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the cash flow equivalent and sensitivity </returns> public static ImmutableMap <Payment, PointSensitivityBuilder> cashFlowEquivalentAndSensitivitySwap(ResolvedSwap swap, RatesProvider ratesProvider) { validateSwap(swap); ImmutableMap <Payment, PointSensitivityBuilder> mapFixed = cashFlowEquivalentAndSensitivityFixedLeg(swap.getLegs(SwapLegType.FIXED).get(0), ratesProvider); ImmutableMap <Payment, PointSensitivityBuilder> mapIbor = cashFlowEquivalentAndSensitivityIborLeg(swap.getLegs(SwapLegType.IBOR).get(0), ratesProvider); return(ImmutableMap.builder <Payment, PointSensitivityBuilder>().putAll(mapFixed).putAll(mapIbor).build()); }
//------------------------------------------------------------------------- /// <summary> /// Computes the present value by replication in SABR framework with extrapolation on the right. /// </summary> /// <param name="cmsPeriod"> the CMS </param> /// <param name="provider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the present value </returns> public CurrencyAmount presentValue(CmsPeriod cmsPeriod, RatesProvider provider, SwaptionVolatilities swaptionVolatilities) { Currency ccy = cmsPeriod.Currency; LocalDate valuationDate = provider.ValuationDate; if (valuationDate.isAfter(cmsPeriod.PaymentDate)) { return(CurrencyAmount.zero(ccy)); } LocalDate fixingDate = cmsPeriod.FixingDate; double dfPayment = provider.discountFactor(ccy, cmsPeriod.PaymentDate); if (!fixingDate.isAfter(valuationDate)) { // Using fixing double?fixedRate = provider.timeSeries(cmsPeriod.Index).get(fixingDate); if (fixedRate.HasValue) { double payoff = 0d; switch (cmsPeriod.CmsPeriodType) { case CAPLET: payoff = Math.Max(fixedRate.Value - cmsPeriod.Strike, 0d); break; case FLOORLET: payoff = Math.Max(cmsPeriod.Strike - fixedRate.Value, 0d); break; case COUPON: payoff = fixedRate.Value; break; default: throw new System.ArgumentException("unsupported CMS type"); } return(CurrencyAmount.of(ccy, payoff * dfPayment * cmsPeriod.Notional * cmsPeriod.YearFraction)); } else if (fixingDate.isBefore(valuationDate)) { throw new System.ArgumentException(Messages.format("Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.Index, fixingDate)); } } if (!cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON)) { throw new System.ArgumentException("Unable to price cap or floor in this pricer"); } // Using forward SwapIndex index = cmsPeriod.Index; ResolvedSwap swap = cmsPeriod.UnderlyingSwap; ResolvedSwapLeg fixedLeg = swap.getLegs(SwapLegType.FIXED).get(0); int nbFixedPaymentYear = (int)(long)Math.Round(1d / ((RatePaymentPeriod)fixedLeg.PaymentPeriods.get(0)).AccrualPeriods.get(0).YearFraction, MidpointRounding.AwayFromZero); int nbFixedPeriod = fixedLeg.PaymentPeriods.size(); double forward = swapPricer.parRate(swap, provider); double tenor = swaptionVolatilities.tenor(swap.StartDate, swap.EndDate); double expiryTime = swaptionVolatilities.relativeTime(fixingDate.atTime(index.FixingTime).atZone(index.FixingZone)); double volatility = swaptionVolatilities.volatility(expiryTime, tenor, forward, forward); ValueDerivatives annuityDerivatives = swapPricer.LegPricer.annuityCash2(nbFixedPaymentYear, nbFixedPeriod, volatility); double forwardAdjustment = -0.5 * forward * forward * volatility * volatility * expiryTime * annuityDerivatives.getDerivative(1) / annuityDerivatives.getDerivative(0); return(CurrencyAmount.of(ccy, (forward + forwardAdjustment) * dfPayment * cmsPeriod.Notional * cmsPeriod.YearFraction)); }