private static void CalculateForecastPaymentAmount(Calculation calculation, FloatingRateCalculation floatingRateCalculation, PaymentCalculationPeriod paymentCalculationPeriod, IRateCurve forecastCurve, IRateCurve discountCurve, DateTime valuationDate) { var amountAccruedPerPaymentPeriod = new List <Money>(); decimal interestFromPreviousPeriods = 0; Notional notionalSchedule = XsdClassesFieldResolver.CalculationGetNotionalSchedule(calculation); Currency notionalCurrency = notionalSchedule.notionalStepSchedule.currency; // Cashflows // foreach (CalculationPeriod calculationPeriod in XsdClassesFieldResolver.GetPaymentCalculationPeriodCalculationPeriodArray(paymentCalculationPeriod)) { decimal notional = XsdClassesFieldResolver.CalculationPeriodGetNotionalAmount(calculationPeriod); decimal finalRate = 0.0m; // If has a fixed rate (fixed rate coupon) // if (XsdClassesFieldResolver.CalculationPeriodHasFixedRate(calculationPeriod)) { finalRate = XsdClassesFieldResolver.CalculationPeriodGetFixedRate(calculationPeriod); } else if (XsdClassesFieldResolver.CalculationPeriodHasFloatingRateDefinition(calculationPeriod)) { if (null != forecastCurve) { FloatingRateDefinition floatingRateDefinition = XsdClassesFieldResolver.CalculationPeriodGetFloatingRateDefinition(calculationPeriod); // Apply spread from schedule if it hasn't been specified yet. // if (!floatingRateDefinition.spreadSpecified) { floatingRateDefinition.spread = floatingRateCalculation.spreadSchedule[0].initialValue; floatingRateDefinition.spreadSpecified = true; } ForecastRateHelper.UpdateFloatingRateDefinition(floatingRateDefinition, floatingRateCalculation, calculation.dayCountFraction, calculationPeriod, forecastCurve); calculationPeriod.Item1 = floatingRateDefinition; decimal calculatedRate = floatingRateDefinition.calculatedRate; // final rate after application of Cap/Floor rates // finalRate = calculatedRate; // If has a Cap rate, finalRate = MAX(0, FinalRate - CapRate) // if (null != floatingRateDefinition.capRate) { Strike strike = floatingRateDefinition.capRate[0]; finalRate = System.Math.Max(0, finalRate - strike.strikeRate); } // If has a Floor rate, finalRate = MAX(0, FloorRate - FinalRate) // if (null != floatingRateDefinition.floorRate) { Strike strike = floatingRateDefinition.floorRate[0]; finalRate = System.Math.Max(0, strike.strikeRate - finalRate); } } } else { throw new System.Exception("CalculationPeriod has neither fixedRate nor floatngRateDefinition."); } // Compound interest accrued during previos calculation periods in this payment period. // decimal notionalAdjustedForInterestFromPreviousPeriods = notional + interestFromPreviousPeriods; if (calculation.discounting == null) { interestFromPreviousPeriods = notionalAdjustedForInterestFromPreviousPeriods * finalRate * calculationPeriod.dayCountYearFraction; } else if (calculation.discounting.discountingType == DiscountingTypeEnum.FRA || calculation.discounting.discountingType == DiscountingTypeEnum.Standard) { interestFromPreviousPeriods = notionalAdjustedForInterestFromPreviousPeriods * (1.0m - 1.0m / (1.0m + finalRate * calculationPeriod.dayCountYearFraction)); } else { throw new NotSupportedException("The specified discountingType is not supported."); } Money amountAccruedPerCalculationPeriod = MoneyHelper.GetAmount(interestFromPreviousPeriods, notionalCurrency); amountAccruedPerPaymentPeriod.Add(amountAccruedPerCalculationPeriod); } paymentCalculationPeriod.forecastPaymentAmount = MoneyHelper.Sum(amountAccruedPerPaymentPeriod); paymentCalculationPeriod.discountFactor = (decimal)discountCurve.GetDiscountFactor(valuationDate, paymentCalculationPeriod.adjustedPaymentDate); paymentCalculationPeriod.discountFactorSpecified = true; paymentCalculationPeriod.presentValueAmount = MoneyHelper.Mul(paymentCalculationPeriod.forecastPaymentAmount, paymentCalculationPeriod.discountFactor); }