/// <summary> /// Calculates the par spread curve sensitivity for a swap. /// <para> /// The par spread is the common spread on all payments of the first leg for which the total swap present value is 0. /// </para> /// <para> /// The par spread is computed with respect to the first leg. For that leg, all the payments have a unique /// accrual period (no compounding) and no FX reset. /// /// </para> /// </summary> /// <param name="swap"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the par spread curve sensitivity of the swap product </returns> public virtual PointSensitivityBuilder parSpreadSensitivity(ResolvedSwap swap, RatesProvider provider) { ResolvedSwapLeg referenceLeg = swap.Legs.get(0); Currency ccyReferenceLeg = referenceLeg.Currency; double convertedPv = presentValue(swap, ccyReferenceLeg, provider).Amount; PointSensitivityBuilder convertedPvDr = presentValueSensitivity(swap, ccyReferenceLeg, provider); // try one payment compounding, typically for inflation swaps Triple <bool, int, double> fixedCompounded = checkFixedCompounded(referenceLeg); if (fixedCompounded.First) { double df = provider.discountFactor(ccyReferenceLeg, referenceLeg.PaymentPeriods.get(0).PaymentDate); PointSensitivityBuilder dfDr = provider.discountFactors(ccyReferenceLeg).zeroRatePointSensitivity(referenceLeg.PaymentPeriods.get(0).PaymentDate); double referenceConvertedPv = legPricer.presentValue(referenceLeg, provider).Amount; PointSensitivityBuilder referenceConvertedPvDr = legPricer.presentValueSensitivity(referenceLeg, provider); double notional = ((RatePaymentPeriod)referenceLeg.PaymentPeriods.get(0)).Notional; PointSensitivityBuilder dParSpreadDr = convertedPvDr.combinedWith(referenceConvertedPvDr.multipliedBy(-1)).multipliedBy(-1.0d / (df * notional)).combinedWith(dfDr.multipliedBy((convertedPv - referenceConvertedPv) / (df * df * notional))).multipliedBy(1.0d / fixedCompounded.Second * Math.Pow(-(convertedPv - referenceConvertedPv) / (df * notional) + 1.0d, 1.0d / fixedCompounded.Second - 1.0d)); return(dParSpreadDr); } double pvbp = legPricer.pvbp(referenceLeg, provider); // Backward sweep double convertedPvBar = -1d / pvbp; double pvbpBar = convertedPv / (pvbp * pvbp); PointSensitivityBuilder pvbpDr = legPricer.pvbpSensitivity(referenceLeg, provider); return(convertedPvDr.multipliedBy(convertedPvBar).combinedWith(pvbpDr.multipliedBy(pvbpBar))); }
//------------------------------------------------------------------------- public virtual void test_presentValue() { CurrencyAmount computedRec = PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, HW_PROVIDER); CurrencyAmount computedPay = PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, HW_PROVIDER); SwapPaymentEventPricer <SwapPaymentEvent> paymentEventPricer = SwapPaymentEventPricer.standard(); ResolvedSwapLeg cashFlowEquiv = CashFlowEquivalentCalculator.cashFlowEquivalentSwap(RSWAP_REC, RATE_PROVIDER); LocalDate expiryDate = MATURITY.toLocalDate(); int nPayments = cashFlowEquiv.PaymentEvents.size(); double[] alpha = new double[nPayments]; double[] discountedCashFlow = new double[nPayments]; for (int loopcf = 0; loopcf < nPayments; loopcf++) { SwapPaymentEvent payment = cashFlowEquiv.PaymentEvents.get(loopcf); alpha[loopcf] = HW_PROVIDER.alpha(RATE_PROVIDER.ValuationDate, expiryDate, expiryDate, payment.PaymentDate); discountedCashFlow[loopcf] = paymentEventPricer.presentValue(payment, RATE_PROVIDER); } double omegaPay = -1d; double kappa = HW_PROVIDER.Model.kappa(DoubleArray.copyOf(discountedCashFlow), DoubleArray.copyOf(alpha)); double expectedRec = 0.0; double expectedPay = 0.0; for (int loopcf = 0; loopcf < nPayments; loopcf++) { expectedRec += discountedCashFlow[loopcf] * NORMAL.getCDF((kappa + alpha[loopcf])); expectedPay += discountedCashFlow[loopcf] * NORMAL.getCDF(omegaPay * (kappa + alpha[loopcf])); } assertEquals(computedRec.Currency, EUR); assertEquals(computedRec.Amount, expectedRec, NOTIONAL * TOL); assertEquals(computedPay.Currency, EUR); assertEquals(computedPay.Amount, expectedPay, NOTIONAL * TOL); }
//----------------------------------------------------------------------- public virtual void fixedSwapLeg() { // a PeriodicSchedule generates a schedule of accrual periods // - interest is accrued every 3 months from 2014-02-12 to 2014-07-31 // - accrual period dates are adjusted "modified following" using the "GBLO" holiday calendar // - there will be a long initial stub // - the regular accrual period dates will be at the end-of-month PeriodicSchedule accrualSchedule = PeriodicSchedule.builder().startDate(LocalDate.of(2014, 2, 12)).endDate(LocalDate.of(2016, 7, 31)).businessDayAdjustment(BusinessDayAdjustment.of(MODIFIED_FOLLOWING, HolidayCalendarIds.GBLO)).frequency(Frequency.P3M).stubConvention(StubConvention.LONG_INITIAL).rollConvention(RollConventions.EOM).build(); // a PaymentSchedule generates a schedule of payment periods, based on the accrual schedule // - payments are every 6 months // - payments are 2 business days after the end of the period // - straight compounding is used (the payments are less frequent than the accrual, so compounding occurs) PaymentSchedule paymentSchedule = PaymentSchedule.builder().paymentFrequency(Frequency.P6M).paymentRelativeTo(PaymentRelativeTo.PERIOD_END).paymentDateOffset(DaysAdjustment.ofBusinessDays(2, HolidayCalendarIds.GBLO)).compoundingMethod(CompoundingMethod.STRAIGHT).build(); // a NotionalSchedule generates a schedule of notional amounts, based on the payment schedule // - in this simple case the notional is 1 million GBP and does not change NotionalSchedule notionalSchedule = NotionalSchedule.of(Currency.GBP, 1_000_000); // a RateCalculationSwapLeg can represent a fixed or floating swap leg // - a FixedRateCalculation is used to represent a fixed rate // - the "Act/Act ISDA" day count is used // - the rate starts at 0.8% and reduces to 0.7% RateCalculationSwapLeg swapLeg = RateCalculationSwapLeg.builder().payReceive(PayReceive.PAY).accrualSchedule(accrualSchedule).paymentSchedule(paymentSchedule).notionalSchedule(notionalSchedule).calculation(FixedRateCalculation.builder().dayCount(DayCounts.ACT_ACT_ISDA).rate(ValueSchedule.of(0.008, ValueStep.of(LocalDate.of(2015, 1, 31), ValueAdjustment.ofReplace(0.007)))).build()).build(); // a ResolvedSwapLeg has all the dates of the cash flows // it remains valid so long as the holiday calendar does not change ResolvedSwapLeg resolvedLeg = swapLeg.resolve(ReferenceData.standard()); Console.WriteLine("===== Fixed ====="); Console.WriteLine(JodaBeanSer.PRETTY.xmlWriter().write(swapLeg)); Console.WriteLine(); Console.WriteLine("===== Fixed resolved ====="); Console.WriteLine(JodaBeanSer.PRETTY.xmlWriter().write(resolvedLeg)); Console.WriteLine(); }
/// <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))); }
// calculate the last fixing date private LocalDate calculateLastFixingDate(LocalDate valuationDate, ReferenceData refData) { SwapTrade trade = template.createTrade(valuationDate, BuySell.BUY, 1, 1, refData); SwapLeg inflationLeg = trade.Product.getLegs(SwapLegType.INFLATION).get(0); ResolvedSwapLeg inflationLegExpanded = inflationLeg.resolve(refData); IList <SwapPaymentPeriod> periods = inflationLegExpanded.PaymentPeriods; int nbPeriods = periods.Count; RatePaymentPeriod lastPeriod = (RatePaymentPeriod)periods[nbPeriods - 1]; IList <RateAccrualPeriod> accruals = lastPeriod.AccrualPeriods; int nbAccruals = accruals.Count; RateAccrualPeriod lastAccrual = accruals[nbAccruals - 1]; if (lastAccrual.RateComputation is InflationMonthlyRateComputation) { return(((InflationMonthlyRateComputation)lastAccrual.RateComputation).EndObservation.FixingMonth.atEndOfMonth()); } if (lastAccrual.RateComputation is InflationInterpolatedRateComputation) { return(((InflationInterpolatedRateComputation)lastAccrual.RateComputation).EndSecondObservation.FixingMonth.atEndOfMonth()); } if (lastAccrual.RateComputation is InflationEndMonthRateComputation) { return(((InflationEndMonthRateComputation)lastAccrual.RateComputation).EndObservation.FixingMonth.atEndOfMonth()); } if (lastAccrual.RateComputation is InflationEndInterpolatedRateComputation) { return(((InflationEndInterpolatedRateComputation)lastAccrual.RateComputation).EndSecondObservation.FixingMonth.atEndOfMonth()); } throw new System.ArgumentException("Rate computation type not supported for last fixing date of an inflation swap."); }
/// <summary> /// Computes cash flow equivalent of Ibor leg. /// <para> /// The return type is {@code ResolvedSwapLeg} in which individual payments are /// represented in terms of {@code NotionalExchange}. /// /// </para> /// </summary> /// <param name="iborLeg"> the Ibor leg </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the cash flow equivalent </returns> public static ResolvedSwapLeg cashFlowEquivalentIborLeg(ResolvedSwapLeg iborLeg, RatesProvider ratesProvider) { ArgChecker.isTrue(iborLeg.Type.Equals(SwapLegType.IBOR), "Leg type should be IBOR"); ArgChecker.isTrue(iborLeg.PaymentEvents.Empty, "PaymentEvent should be empty"); IList <NotionalExchange> paymentEvents = new List <NotionalExchange>(); foreach (SwapPaymentPeriod paymentPeriod in iborLeg.PaymentPeriods) { ArgChecker.isTrue(paymentPeriod is RatePaymentPeriod, "rate payment should be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod)paymentPeriod; ArgChecker.isTrue(ratePaymentPeriod.AccrualPeriods.size() == 1, "rate payment should not be compounding"); RateAccrualPeriod rateAccrualPeriod = ratePaymentPeriod.AccrualPeriods.get(0); CurrencyAmount notional = ratePaymentPeriod.NotionalAmount; LocalDate paymentDate = ratePaymentPeriod.PaymentDate; IborIndexObservation obs = ((IborRateComputation)rateAccrualPeriod.RateComputation).Observation; IborIndex index = obs.Index; LocalDate fixingStartDate = obs.EffectiveDate; double fixingYearFraction = obs.YearFraction; double beta = (1d + fixingYearFraction * ratesProvider.iborIndexRates(index).rate(obs)) * ratesProvider.discountFactor(paymentPeriod.Currency, paymentPeriod.PaymentDate) / ratesProvider.discountFactor(paymentPeriod.Currency, fixingStartDate); double ycRatio = rateAccrualPeriod.YearFraction / fixingYearFraction; NotionalExchange payStart = NotionalExchange.of(notional.multipliedBy(beta * ycRatio), fixingStartDate); NotionalExchange payEnd = NotionalExchange.of(notional.multipliedBy(-ycRatio), paymentDate); paymentEvents.Add(payStart); paymentEvents.Add(payEnd); } ResolvedSwapLeg leg = ResolvedSwapLeg.builder().paymentEvents(paymentEvents).payReceive(PayReceive.RECEIVE).type(SwapLegType.OTHER).build(); return(leg); }
//------------------------------------------------------------------------- /// <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); double pvbp = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider); double strike = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp); 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; // Payer at strike is exercise when rate > strike, i.e. call on rate // Backward sweep PointSensitivityBuilder pvbpDr = SwapPricer.LegPricer.pvbpSensitivity(fixedLeg, ratesProvider); PointSensitivityBuilder forwardDr = SwapPricer.parRateSensitivity(underlying, ratesProvider); 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); double sign = swaption.LongShort.sign(); return(pvbpDr.multipliedBy(price * sign * Math.Sign(pvbp)).combinedWith(forwardDr.multipliedBy((delta + vega * volatilityAdj.getDerivative(0)) * Math.Abs(pvbp) * sign))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity to the SABR model parameters of the swaption product. /// <para> /// The sensitivity of the present value to the SABR model parameters, alpha, beta, rho and nu. /// /// </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 SABR model parameters </returns> public virtual PointSensitivityBuilder presentValueSensitivityModelParamsSabr(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double shift = swaptionVolatilities.shift(expiry, tenor); double pvbp = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider); double strike = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); DoubleArray derivative = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward).Derivatives; // Backward sweep double vega = Math.Abs(pvbp) * BlackFormulaRepository.vega(forward + shift, strike + shift, expiry, volatility) * swaption.LongShort.sign(); // sensitivities Currency ccy = fixedLeg.Currency; SwaptionVolatilitiesName name = swaptionVolatilities.Name; return(PointSensitivityBuilder.of(SwaptionSabrSensitivity.of(name, expiry, tenor, ALPHA, ccy, vega * derivative.get(2)), SwaptionSabrSensitivity.of(name, expiry, tenor, BETA, ccy, vega * derivative.get(3)), SwaptionSabrSensitivity.of(name, expiry, tenor, RHO, ccy, vega * derivative.get(4)), SwaptionSabrSensitivity.of(name, expiry, tenor, NU, ccy, vega * derivative.get(5)))); }
private ResolvedCms(ResolvedCmsLeg cmsLeg, ResolvedSwapLeg payLeg) { JodaBeanUtils.notNull(cmsLeg, "cmsLeg"); this.cmsLeg = cmsLeg; this.payLeg = payLeg; validate(); }
/// <summary> /// Explain present value for a swap leg. /// </summary> /// <param name="leg"> the swap log </param> /// <param name="provider"> the rates provider </param> /// <returns> the explain PV map </returns> public virtual ExplainMap explainPresentValue(ResolvedSwapLeg leg, RatesProvider provider) { ExplainMapBuilder builder = ExplainMap.builder(); explainPresentValueInternal(leg, provider, builder); return(builder.build()); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the /// curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential /// change of volatility due to the implicit change of forward rate or moneyness. /// /// </para> /// </summary> /// <param name="swaption"> the swaption </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 presentValueSensitivityRatesStickyStrike(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); 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 volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.PayReceive.Receive); double price = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility); double delta = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility); // Backward sweep 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 + annuityCashDr * price)).combinedWith(discountSettleSensi.multipliedBy(sign * annuityCash * price))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the future cash flows of the swap leg. /// <para> /// Each expected cash flow is added to the result. /// This is based on <seealso cref="#forecastValue(ResolvedSwapLeg, RatesProvider)"/>. /// /// </para> /// </summary> /// <param name="leg"> the swap leg for which the cash flows should be computed </param> /// <param name="provider"> the rates provider </param> /// <returns> the cash flows </returns> public virtual CashFlows cashFlows(ResolvedSwapLeg leg, RatesProvider provider) { CashFlows cashFlowPeriods = cashFlowPeriodsInternal(leg, provider); CashFlows cashFlowEvents = cashFlowEventsInternal(leg, provider); return(cashFlowPeriods.combinedWith(cashFlowEvents)); }
//----------------------------------------------------------------------- public virtual void floatingSwapLeg() { // a PeriodicSchedule generates a schedule of accrual periods // - interest is accrued every 6 months from 2014-02-12 to 2014-07-31 // - accrual period dates are adjusted "modified following" using the "GBLO" holiday calendar // - there will be a long initial stub // - the regular accrual period dates will be at the end-of-month PeriodicSchedule accrualSchedule = PeriodicSchedule.builder().startDate(LocalDate.of(2014, 2, 12)).endDate(LocalDate.of(2016, 7, 31)).businessDayAdjustment(BusinessDayAdjustment.of(MODIFIED_FOLLOWING, HolidayCalendarIds.GBLO)).frequency(Frequency.P6M).stubConvention(StubConvention.LONG_INITIAL).rollConvention(RollConventions.EOM).build(); // a PaymentSchedule generates a schedule of payment periods, based on the accrual schedule // - payments are every 6 months // - payments are 2 business days after the end of the period // - no compounding is needed as the payment schedule matches the accrual schedule PaymentSchedule paymentSchedule = PaymentSchedule.builder().paymentFrequency(Frequency.P6M).paymentRelativeTo(PaymentRelativeTo.PERIOD_END).paymentDateOffset(DaysAdjustment.ofBusinessDays(2, HolidayCalendarIds.GBLO)).build(); // a NotionalSchedule generates a schedule of notional amounts, based on the payment schedule // - in this simple case the notional is 1 million GBP and does not change NotionalSchedule notionalSchedule = NotionalSchedule.of(Currency.GBP, 1_000_000); // a RateCalculationSwapLeg can represent a fixed or floating swap leg // - an IborRateCalculation is used to represent a floating Ibor rate // - the "Act/Act ISDA" day count is used // - the index is GBP LIBOR 6M // - fixing is 2 days before the start of the period using the "GBLO" holiday calendar RateCalculationSwapLeg swapLeg = RateCalculationSwapLeg.builder().payReceive(PayReceive.RECEIVE).accrualSchedule(accrualSchedule).paymentSchedule(paymentSchedule).notionalSchedule(notionalSchedule).calculation(IborRateCalculation.builder().dayCount(DayCounts.ACT_ACT_ISDA).index(IborIndices.GBP_LIBOR_6M).fixingRelativeTo(FixingRelativeTo.PERIOD_START).fixingDateOffset(DaysAdjustment.ofBusinessDays(-2, HolidayCalendarIds.GBLO)).build()).build(); // a ResolvedSwapLeg has all the dates of the cash flows // it remains valid so long as the holiday calendar does not change ResolvedSwapLeg resolvedLeg = swapLeg.resolve(ReferenceData.standard()); Console.WriteLine("===== Floating ====="); Console.WriteLine(JodaBeanSer.PRETTY.xmlWriter().write(swapLeg)); Console.WriteLine(); Console.WriteLine("===== Floating resolved ====="); Console.WriteLine(JodaBeanSer.PRETTY.xmlWriter().write(resolvedLeg)); Console.WriteLine(); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the /// curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential /// change of volatility due to the implicit change of forward rate or moneyness. /// /// </para> /// </summary> /// <param name="swaption"> the swaption </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 presentValueSensitivityRatesStickyStrike(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); 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); double pvbp = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider); double strike = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.PayReceive.Receive); double price = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility); double delta = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility); // Backward sweep PointSensitivityBuilder pvbpDr = SwapPricer.LegPricer.pvbpSensitivity(fixedLeg, ratesProvider); PointSensitivityBuilder forwardDr = SwapPricer.parRateSensitivity(underlying, ratesProvider); double sign = swaption.LongShort.sign(); return(pvbpDr.multipliedBy(price * sign * Math.Sign(pvbp)).combinedWith(forwardDr.multipliedBy(delta * Math.Abs(pvbp) * sign))); }
/// <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); }
//------------------------------------------------------------------------- /// <summary> /// Computes the Present Value of a Basis Point for a swap leg. /// <para> /// The Present Value of a Basis Point is the value of the leg when the rate is equal to 1. /// A better name would be "Present Value of 1". /// The quantity is also known as "physical annuity" or "level". /// </para> /// <para> /// Each period must not have FX reset or compounding. /// They must not be of type <seealso cref="KnownAmountSwapPaymentPeriod"/>. /// /// </para> /// </summary> /// <param name="leg"> the swap leg </param> /// <param name="provider"> the rates provider </param> /// <returns> the Present Value of a Basis Point </returns> public virtual double pvbp(ResolvedSwapLeg leg, RatesProvider provider) { double pvbpLeg = 0d; foreach (SwapPaymentPeriod period in leg.PaymentPeriods) { pvbpLeg += paymentPeriodPricer.pvbp(period, provider); } return(pvbpLeg); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the Present Value of a Basis Point curve sensitivity for a fixed swap leg. /// <para> /// The Present Value of a Basis Point is the value of the leg when the rate is equal to 1. /// A better name would be "Present Value of 1". /// The quantity is also known as "physical annuity" or "level". /// </para> /// <para> /// Each period must not have FX reset or compounding. /// They must not be of type <seealso cref="KnownAmountSwapPaymentPeriod"/>. /// /// </para> /// </summary> /// <param name="fixedLeg"> the swap fixed leg </param> /// <param name="provider"> the rates provider </param> /// <returns> the Present Value of a Basis Point sensitivity to the curves </returns> public virtual PointSensitivityBuilder pvbpSensitivity(ResolvedSwapLeg fixedLeg, RatesProvider provider) { PointSensitivityBuilder builder = PointSensitivityBuilder.none(); foreach (SwapPaymentPeriod period in fixedLeg.PaymentPeriods) { builder = builder.combinedWith(paymentPeriodPricer.pvbpSensitivity(period, provider)); } return(builder); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the accrued interest since the last payment. /// <para> /// This determines the payment period applicable at the valuation date and calculates /// the accrued interest since the last payment. /// The result is returned using the payment currency of the leg. /// /// </para> /// </summary> /// <param name="leg"> the leg </param> /// <param name="provider"> the rates provider </param> /// <returns> the accrued interest of the swap leg </returns> public virtual CurrencyAmount accruedInterest(ResolvedSwapLeg leg, RatesProvider provider) { Optional <SwapPaymentPeriod> period = leg.findPaymentPeriod(provider.ValuationDate); if (period.Present) { double accruedInterest = paymentPeriodPricer.accruedInterest(period.get(), provider); return(CurrencyAmount.of(leg.Currency, accruedInterest)); } return(CurrencyAmount.zero(leg.Currency)); }
/// <summary> /// Calculates the strike. /// </summary> /// <param name="fixedLeg"> the fixed leg </param> /// <returns> the strike </returns> protected internal virtual double calculateStrike(ResolvedSwapLeg fixedLeg) { SwapPaymentPeriod paymentPeriod = fixedLeg.PaymentPeriods.get(0); ArgChecker.isTrue(paymentPeriod is RatePaymentPeriod, "Payment period must be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod)paymentPeriod; // compounding is caught when par rate is computed RateComputation rateComputation = ratePaymentPeriod.AccrualPeriods.get(0).RateComputation; ArgChecker.isTrue(rateComputation is FixedRateComputation, "Swap leg must be fixed leg"); return(((FixedRateComputation)rateComputation).Rate); }
//------------------------------------------------------------------------- //JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @ImmutableConstructor private ResolvedIborCapFloor(ResolvedIborCapFloorLeg capFloorLeg, com.opengamma.strata.product.swap.ResolvedSwapLeg payLeg) private ResolvedIborCapFloor(ResolvedIborCapFloorLeg capFloorLeg, ResolvedSwapLeg payLeg) { JodaBeanUtils.notNull(capFloorLeg, "capFloorLeg"); if (payLeg != null) { ArgChecker.isFalse(payLeg.PayReceive.Equals(capFloorLeg.PayReceive), "Legs must have different Pay/Receive flag, but both were {}", payLeg.PayReceive); } this.capFloorLeg = capFloorLeg; this.payLeg = payLeg; this.currencies = buildCurrencies(capFloorLeg, payLeg); this.indices = buildIndices(capFloorLeg, payLeg); }
// find the notional private CurrencyAmount buildLegNotional(ResolvedSwapLeg leg) { // check for NotionalPaymentPeriod SwapPaymentPeriod firstPaymentPeriod = leg.PaymentPeriods.get(0); if (firstPaymentPeriod is NotionalPaymentPeriod) { NotionalPaymentPeriod pp = (NotionalPaymentPeriod)firstPaymentPeriod; return(pp.NotionalAmount.positive()); } return(NOT_FOUND); }
/// <summary> /// Computes the derivative of the conventional cash annuity with respect to the yield from a swap leg. /// <para> /// 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. /// /// </para> /// </summary> /// <param name="fixedLeg"> the fixed leg of the swap </param> /// <param name="yield"> the yield </param> /// <returns> the cash annuity </returns> public virtual ValueDerivatives annuityCashDerivative(ResolvedSwapLeg fixedLeg, double yield) { int nbFixedPeriod = fixedLeg.PaymentPeriods.size(); SwapPaymentPeriod paymentPeriod = fixedLeg.PaymentPeriods.get(0); ArgChecker.isTrue(paymentPeriod is RatePaymentPeriod, "payment period should be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod)paymentPeriod; int nbFixedPaymentYear = (int)(long)Math.Round(1d / ratePaymentPeriod.DayCount.yearFraction(ratePaymentPeriod.StartDate, ratePaymentPeriod.EndDate), MidpointRounding.AwayFromZero); double notional = Math.Abs(ratePaymentPeriod.Notional); ValueDerivatives annuityUnit = annuityCash1(nbFixedPaymentYear, nbFixedPeriod, yield); return(ValueDerivatives.of(annuityUnit.Value * notional, annuityUnit.Derivatives.multipliedBy(notional))); }
// calculates the present value of the events composing the leg in the currency of the swap leg internal virtual double presentValueEventsInternal(ResolvedSwapLeg leg, RatesProvider provider) { double total = 0d; foreach (SwapPaymentEvent @event in leg.PaymentEvents) { if ([email protected](provider.ValuationDate)) { total += paymentEventPricer.presentValue(@event, provider); } } return(total); }
// calculates the present value of the periods composing the leg in the currency of the swap leg internal virtual double presentValuePeriodsInternal(ResolvedSwapLeg leg, RatesProvider provider) { double total = 0d; foreach (SwapPaymentPeriod period in leg.PaymentPeriods) { if (!period.PaymentDate.isBefore(provider.ValuationDate)) { total += paymentPeriodPricer.presentValue(period, provider); } } return(total); }
private MultiCurrencyAmount currencyExposureEventsInternal(ResolvedSwapLeg leg, RatesProvider provider) { MultiCurrencyAmount total = MultiCurrencyAmount.empty(); foreach (SwapPaymentEvent @event in leg.PaymentEvents) { if ([email protected](provider.ValuationDate)) { total = total.plus(paymentEventPricer.currencyExposure(@event, provider)); } } return(total); }
private double currentCashPeriodsInternal(ResolvedSwapLeg leg, RatesProvider provider) { double total = 0d; foreach (SwapPaymentPeriod period in leg.PaymentPeriods) { if (!period.PaymentDate.isBefore(provider.ValuationDate)) { total += paymentPeriodPricer.currentCash(period, provider); } } return(total); }
private double currentCashEventsInternal(ResolvedSwapLeg leg, RatesProvider provider) { double total = 0d; foreach (SwapPaymentEvent @event in leg.PaymentEvents) { if ([email protected](provider.ValuationDate)) { total += paymentEventPricer.currentCash(@event, provider); } } return(total); }
//------------------------------------------------------------------------- /// <summary> /// Computes the conventional cash annuity from a swap leg. /// <para> /// 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. /// /// </para> /// </summary> /// <param name="fixedLeg"> the fixed leg of the swap </param> /// <param name="yield"> the yield </param> /// <returns> the cash annuity </returns> public virtual double annuityCash(ResolvedSwapLeg fixedLeg, double yield) { int nbFixedPeriod = fixedLeg.PaymentPeriods.size(); SwapPaymentPeriod paymentPeriod = fixedLeg.PaymentPeriods.get(0); ArgChecker.isTrue(paymentPeriod is RatePaymentPeriod, "payment period should be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod)paymentPeriod; int nbFixedPaymentYear = (int)(long)Math.Round(1d / ratePaymentPeriod.DayCount.yearFraction(ratePaymentPeriod.StartDate, ratePaymentPeriod.EndDate), MidpointRounding.AwayFromZero); double notional = Math.Abs(ratePaymentPeriod.Notional); double annuityCash = notional * this.annuityCash(nbFixedPaymentYear, nbFixedPeriod, yield); return(annuityCash); }
//------------------------------------------------------------------------- /// <summary> /// Computes the implied volatility of the swaption. /// </summary> /// <param name="swaption"> the swaption </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the implied volatility associated with the swaption </returns> public virtual double impliedVolatility(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); ArgChecker.isTrue(expiry >= 0d, "Option must be before expiry to compute an implied volatility"); double forward = SwapPricer.parRate(underlying, ratesProvider); double strike = calculateStrike(fixedLeg); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); return(swaptionVolatilities.volatility(expiry, tenor, strike, forward)); }
// calculate the last fixing date private LocalDate calculateLastFixingDate(LocalDate valuationDate, ReferenceData refData) { SwapTrade trade = template.createTrade(valuationDate, BuySell.BUY, 1, 1, refData); SwapLeg iborLeg = trade.Product.getLegs(SwapLegType.IBOR).get(0); ResolvedSwapLeg iborLegExpanded = iborLeg.resolve(refData); IList <SwapPaymentPeriod> periods = iborLegExpanded.PaymentPeriods; int nbPeriods = periods.Count; RatePaymentPeriod lastPeriod = (RatePaymentPeriod)periods[nbPeriods - 1]; IList <RateAccrualPeriod> accruals = lastPeriod.AccrualPeriods; int nbAccruals = accruals.Count; IborRateComputation ibor = (IborRateComputation)accruals[nbAccruals - 1].RateComputation; return(ibor.FixingDate); }