// 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 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); }
/// <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))); }
//------------------------------------------------------------------------- /// <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); }
// 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); }
//------------------------------------------------------------------------- /// <summary> /// Computes the par rate for swaps with a fixed leg. /// <para> /// The par rate is the common rate on all payments of the fixed leg for which the total swap present value is 0. /// </para> /// <para> /// At least one leg must be a fixed leg. The par rate will be computed with respect to the first fixed leg /// in which all the payments are fixed payments with a unique accrual period (no compounding) and no FX reset. /// If the fixed leg is compounding, the par rate is computed only when the number of fixed coupon payments is 1 and /// accrual factor of each sub-period is 1 /// /// </para> /// </summary> /// <param name="swap"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the par rate </returns> public virtual double parRate(ResolvedSwap swap, RatesProvider provider) { // find fixed leg ResolvedSwapLeg fixedLeg = this.fixedLeg(swap); Currency ccyFixedLeg = fixedLeg.Currency; // other payments (not fixed leg coupons) converted in fixed leg currency double otherLegsConvertedPv = 0.0; foreach (ResolvedSwapLeg leg in swap.Legs) { if (leg != fixedLeg) { double pvLocal = legPricer.presentValueInternal(leg, provider); otherLegsConvertedPv += (pvLocal * provider.fxRate(leg.Currency, ccyFixedLeg)); } } double fixedLegEventsPv = legPricer.presentValueEventsInternal(fixedLeg, provider); if (fixedLeg.PaymentPeriods.size() > 1) { // try multiperiod par-rate // PVBP double pvbpFixedLeg = legPricer.pvbp(fixedLeg, provider); // Par rate return(-(otherLegsConvertedPv + fixedLegEventsPv) / pvbpFixedLeg); } SwapPaymentPeriod firstPeriod = fixedLeg.PaymentPeriods.get(0); ArgChecker.isTrue(firstPeriod is RatePaymentPeriod, "PaymentPeriod must be instance of RatePaymentPeriod"); RatePaymentPeriod payment = (RatePaymentPeriod)firstPeriod; if (payment.AccrualPeriods.size() == 1) { // no compounding // PVBP double pvbpFixedLeg = legPricer.pvbp(fixedLeg, provider); // Par rate return(-(otherLegsConvertedPv + fixedLegEventsPv) / pvbpFixedLeg); } // try Compounding Triple <bool, int, double> fixedCompounded = checkFixedCompounded(fixedLeg); ArgChecker.isTrue(fixedCompounded.First, "Swap should have a fixed leg and for one payment it should be based on compunding witout spread."); double notional = payment.Notional; double df = provider.discountFactor(ccyFixedLeg, payment.PaymentDate); return(Math.Pow(-(otherLegsConvertedPv + fixedLegEventsPv) / (notional * df) + 1.0d, 1.0 / fixedCompounded.Second) - 1.0d); }
// Checks if the leg is a fixed leg with one payment and compounding // This type of leg is used in zero-coupon inflation swaps // When returning a 'true' for the first element, the second element is the number of periods which are used in // par rate/spread computation and the third element is the common fixed rate private Triple <bool, int, double> checkFixedCompounded(ResolvedSwapLeg leg) { if (leg.PaymentEvents.size() != 0) { return(Triple.of(false, 0, 0.0d)); // No event } RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod)leg.PaymentPeriods.get(0); if (ratePaymentPeriod.CompoundingMethod == CompoundingMethod.NONE) { return(Triple.of(false, 0, 0.0d)); // Should be compounded } ImmutableList <RateAccrualPeriod> accrualPeriods = ratePaymentPeriod.AccrualPeriods; int nbAccrualPeriods = accrualPeriods.size(); double fixedRate = 0; for (int i = 0; i < nbAccrualPeriods; i++) { if (!(accrualPeriods.get(i).RateComputation is FixedRateComputation)) { return(Triple.of(false, 0, 0.0d)); // Should be fixed period } if ((i > 0) && (((FixedRateComputation)accrualPeriods.get(i).RateComputation).Rate != fixedRate)) { return(Triple.of(false, 0, 0.0d)); // All fixed rates should be the same } fixedRate = ((FixedRateComputation)accrualPeriods.get(i).RateComputation).Rate; if (accrualPeriods.get(i).Spread != 0) { return(Triple.of(false, 0, 0.0d)); // Should have no spread } if (accrualPeriods.get(i).Gearing != 1.0d) { return(Triple.of(false, 0, 0.0d)); // Should have a gearing of 1. } if (accrualPeriods.get(i).YearFraction != 1.0d) { return(Triple.of(false, 0, 0.0d)); // Should have a year fraction of 1. } } return(Triple.of(true, nbAccrualPeriods, fixedRate)); }
/// <summary> /// Computes cash flow equivalent and sensitivity of fixed leg. /// <para> /// The return type is a map of {@code NotionalExchange} and {@code PointSensitivityBuilder}. /// /// </para> /// </summary> /// <param name="fixedLeg"> the fixed leg </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the cash flow equivalent and sensitivity </returns> public static ImmutableMap <Payment, PointSensitivityBuilder> cashFlowEquivalentAndSensitivityFixedLeg(ResolvedSwapLeg fixedLeg, RatesProvider ratesProvider) { ArgChecker.isTrue(fixedLeg.Type.Equals(SwapLegType.FIXED), "Leg type should be FIXED"); ArgChecker.isTrue(fixedLeg.PaymentEvents.Empty, "PaymentEvent should be empty"); IDictionary <Payment, PointSensitivityBuilder> res = new Dictionary <Payment, PointSensitivityBuilder>(); foreach (SwapPaymentPeriod paymentPeriod in fixedLeg.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); double factor = rateAccrualPeriod.YearFraction * ((FixedRateComputation)rateAccrualPeriod.RateComputation).Rate; CurrencyAmount notional = ratePaymentPeriod.NotionalAmount.multipliedBy(factor); LocalDate paymentDate = ratePaymentPeriod.PaymentDate; Payment pay = Payment.of(notional, paymentDate); res[pay] = PointSensitivityBuilder.none(); } return(ImmutableMap.copyOf(res)); }
/// <summary> /// Computes cash flow equivalent of fixed leg. /// <para> /// The return type is {@code ResolvedSwapLeg} in which individual payments are /// represented in terms of {@code NotionalExchange}. /// /// </para> /// </summary> /// <param name="fixedLeg"> the fixed leg </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the cash flow equivalent </returns> public static ResolvedSwapLeg cashFlowEquivalentFixedLeg(ResolvedSwapLeg fixedLeg, RatesProvider ratesProvider) { ArgChecker.isTrue(fixedLeg.Type.Equals(SwapLegType.FIXED), "Leg type should be FIXED"); ArgChecker.isTrue(fixedLeg.PaymentEvents.Empty, "PaymentEvent should be empty"); IList <NotionalExchange> paymentEvents = new List <NotionalExchange>(); foreach (SwapPaymentPeriod paymentPeriod in fixedLeg.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); double factor = rateAccrualPeriod.YearFraction * ((FixedRateComputation)rateAccrualPeriod.RateComputation).Rate; CurrencyAmount notional = ratePaymentPeriod.NotionalAmount.multipliedBy(factor); LocalDate paymentDate = ratePaymentPeriod.PaymentDate; NotionalExchange pay = NotionalExchange.of(notional, paymentDate); paymentEvents.Add(pay); } ResolvedSwapLeg leg = ResolvedSwapLeg.builder().paymentEvents(paymentEvents).payReceive(PayReceive.RECEIVE).type(SwapLegType.OTHER).build(); return(leg); }
/// <summary> /// Computes cash flow equivalent and sensitivity of Ibor leg. /// <para> /// The return type is a map of {@code NotionalExchange} and {@code PointSensitivityBuilder}. /// /// </para> /// </summary> /// <param name="iborLeg"> the Ibor leg </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the cash flow equivalent and sensitivity </returns> public static ImmutableMap <Payment, PointSensitivityBuilder> cashFlowEquivalentAndSensitivityIborLeg(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"); IDictionary <Payment, PointSensitivityBuilder> res = new Dictionary <Payment, PointSensitivityBuilder>(); 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 factorIndex = (1d + fixingYearFraction * ratesProvider.iborIndexRates(index).rate(obs)); double dfPayment = ratesProvider.discountFactor(paymentPeriod.Currency, paymentPeriod.PaymentDate); double dfStart = ratesProvider.discountFactor(paymentPeriod.Currency, fixingStartDate); double beta = factorIndex * dfPayment / dfStart; double ycRatio = rateAccrualPeriod.YearFraction / fixingYearFraction; Payment payStart = Payment.of(notional.multipliedBy(beta * ycRatio), fixingStartDate); Payment payEnd = Payment.of(notional.multipliedBy(-ycRatio), paymentDate); double factor = ycRatio * notional.Amount / dfStart; PointSensitivityBuilder factorIndexSensi = ratesProvider.iborIndexRates(index).ratePointSensitivity(obs).multipliedBy(fixingYearFraction * dfPayment * factor); PointSensitivityBuilder dfPaymentSensitivity = ratesProvider.discountFactors(paymentPeriod.Currency).zeroRatePointSensitivity(paymentPeriod.PaymentDate).multipliedBy(factorIndex * factor); PointSensitivityBuilder dfStartSensitivity = ratesProvider.discountFactors(paymentPeriod.Currency).zeroRatePointSensitivity(fixingStartDate).multipliedBy(-factorIndex * dfPayment * factor / dfStart); res[payStart] = factorIndexSensi.combinedWith(dfPaymentSensitivity).combinedWith(dfStartSensitivity); res[payEnd] = PointSensitivityBuilder.none(); } return(ImmutableMap.copyOf(res)); }