private static void GeneratePeriodsBackward(DateTime firstRollDate, CalculationPeriodFrequency frequency,
                                                    BusinessDayAdjustments calculationPeriodDatesAdjustments,
                                                    CalculationPeriodsPrincipalExchangesAndStubs result,
                                                    DateTime adjustedEffectiveDate, StubPeriodTypeEnum?initialStubType
                                                    , IBusinessCalendar paymentCalendar)
        {
            DateTime periodEndDate               = firstRollDate;
            DateTime periodStartDate             = AddPeriod(periodEndDate, IntervalHelper.FromFrequency(frequency), -1);
            bool     encounteredShortInitialStub = false;

            //if (paymentCalendar == null)
            //{
            //    paymentCalendar = BusinessCenterHelper.ToBusinessCalendar(cache, calculationPeriodDatesAdjustments.businessCenters);
            //}
            do
            {
                var calculationPeriod = new CalculationPeriod();
                //  Apply ROLL CONVENTION (NOT A BUSINESS DAY CONVENTION!) to unadjusted period start and end dates.
                //
                DateTime rollConventionAdjustedPeriodStartDate = periodStartDate;
                DateTime rollConventionAdjustedPeriodEndDate   = periodEndDate;
                var      frequencyPeriod = EnumHelper.Parse <PeriodEnum>(frequency.period, true);
                if (frequencyPeriod != PeriodEnum.D)//adjust if the frequency is NOT expressed in days
                {
                    rollConventionAdjustedPeriodStartDate = RollConventionEnumHelper.AdjustDate(frequency.rollConvention, periodStartDate);
                    rollConventionAdjustedPeriodEndDate   = RollConventionEnumHelper.AdjustDate(frequency.rollConvention, periodEndDate);
                }
                CalculationPeriodHelper.SetUnadjustedDates(calculationPeriod, rollConventionAdjustedPeriodStartDate, rollConventionAdjustedPeriodEndDate);
                //  Set adjusted period dates
                //
                DateTime adjustedPeriodStartDate = AdjustedDateHelper.ToAdjustedDate(paymentCalendar, rollConventionAdjustedPeriodStartDate, calculationPeriodDatesAdjustments);
                DateTime adjustedPeriodEndDate   = AdjustedDateHelper.ToAdjustedDate(paymentCalendar, rollConventionAdjustedPeriodEndDate, calculationPeriodDatesAdjustments);
                CalculationPeriodHelper.SetAdjustedDates(calculationPeriod, adjustedPeriodStartDate, adjustedPeriodEndDate);
                //if (calculationPeriod.unadjustedStartDate > adjustedEffectiveDate)
                if (calculationPeriod.adjustedStartDate > adjustedEffectiveDate)
                {
                    result.InsertFirst(calculationPeriod);
                    periodEndDate   = periodStartDate;
                    periodStartDate = AddPeriod(periodEndDate, IntervalHelper.FromFrequency(frequency), -1);
                }
                //else if (calculationPeriod.unadjustedStartDate == adjustedEffectiveDate)//first period - not stub
                else if (calculationPeriod.adjustedStartDate == adjustedEffectiveDate)//first period - not stub
                {
                    result.InsertFirst(calculationPeriod);
                    break;
                }
                else//first period - short stub (merge with next period if a long stub specified)
                {
                    encounteredShortInitialStub = true;
                    //calculationPeriod.unadjustedStartDate = adjustedEffectiveDate;
                    calculationPeriod.adjustedStartDate = adjustedEffectiveDate;
                    result.InitialStubCalculationPeriod = calculationPeriod;
                    break;
                }
            } while (true);
            if (encounteredShortInitialStub && initialStubType == StubPeriodTypeEnum.LongInitial)
            {
                result.CreateLongInitialStub();
            }
        }
        /// <summary>
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="interestRateStream"></param>
        /// <param name="listCalculationPeriods"></param>
        /// <param name="fixingCalendar"></param>
        /// <param name="nameSpace"></param>
        /// <returns></returns>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public static List <DateTime> GetAdjustedResetDates(ICoreCache cache, InterestRateStream interestRateStream,
                                                            List <CalculationPeriod> listCalculationPeriods, IBusinessCalendar fixingCalendar, string nameSpace)
        {
            var        adjustedResetDates         = new List <DateTime>();
            ResetDates resetDates                 = interestRateStream.resetDates;
            Period     resetFrequency             = IntervalHelper.FromFrequency(resetDates.resetFrequency);
            Period     calculationPeriodFrequency = IntervalHelper.FromFrequency(interestRateStream.calculationPeriodDates.calculationPeriodFrequency);

            if (resetFrequency.period != calculationPeriodFrequency.period)
            {
                throw new NotSupportedException(
                          $"Reset period type ({resetFrequency.period}) and calculation period type ({calculationPeriodFrequency.period}) are different. This is not supported.");
            }

            if (int.Parse(resetFrequency.periodMultiplier) != int.Parse(calculationPeriodFrequency.periodMultiplier))
            {
                throw new NotSupportedException(
                          $"Reset period frequency ({resetFrequency.period}) is not equal to calculation period frequency ({calculationPeriodFrequency.period}). This is not supported.");
            }
            BusinessDayAdjustments resetDatesAdjustments = resetDates.resetDatesAdjustments;
            ResetRelativeToEnum    resetRelativeTo       = resetDates.resetRelativeTo;

            if (fixingCalendar == null)
            {
                fixingCalendar = BusinessCenterHelper.ToBusinessCalendar(cache, resetDatesAdjustments.businessCenters, nameSpace);
            }
            foreach (CalculationPeriod calculationPeriodsInPaymentPeriod in listCalculationPeriods)
            {
                switch (resetRelativeTo)
                {
                case ResetRelativeToEnum.CalculationPeriodStartDate:
                {
                    DateTime unadjustedResetDate = calculationPeriodsInPaymentPeriod.unadjustedStartDate;
                    DateTime adjustedResetDate   = AdjustedDateHelper.ToAdjustedDate(fixingCalendar, unadjustedResetDate, resetDatesAdjustments);
                    adjustedResetDates.Add(adjustedResetDate);
                    break;
                }

                case ResetRelativeToEnum.CalculationPeriodEndDate:
                {
                    DateTime unadjustedResetDate = calculationPeriodsInPaymentPeriod.unadjustedEndDate;
                    DateTime adjustedResetDate   = AdjustedDateHelper.ToAdjustedDate(fixingCalendar, unadjustedResetDate, resetDatesAdjustments);
                    adjustedResetDates.Add(adjustedResetDate);
                    break;
                }

                default:
                {
                    throw new NotImplementedException("resetRelativeTo");
                }
                }
            }
            return(adjustedResetDates);
        }
        public static void GeneratePeriods(DateTime unadjustedStartDate,
                                           DateTime unadjustedTerminationDate,
                                           DateTime?firstUnadjustedRegularPeriodStartDate,
                                           DateTime?lastUnadjustedRegularPeriodEndDate,
                                           //StubPeriodTypeEnum? initialStubType,
                                           //StubPeriodTypeEnum? finalStubType,
                                           CalculationPeriodFrequency frequency,
                                           BusinessDayAdjustments calculationPeriodDatesAdjustments,
                                           CalculationPeriodsPrincipalExchangesAndStubs result,
                                           IBusinessCalendar paymentCalendar)
        {
            DateTime periodEndDate = unadjustedTerminationDate;
            DateTime periodStartDate;
            bool     isInitialStub = firstUnadjustedRegularPeriodStartDate != null;
            var      startDate     = unadjustedStartDate;

            if (isInitialStub)
            {
                startDate = (DateTime)firstUnadjustedRegularPeriodStartDate;
            }
            var isFinalStub = lastUnadjustedRegularPeriodEndDate != null;

            if (isFinalStub)
            {
                periodStartDate = (DateTime)lastUnadjustedRegularPeriodEndDate;
            }
            else
            {
                periodStartDate = AddPeriod(periodEndDate, IntervalHelper.FromFrequency(frequency), -1);
            }
            var isFirstPeriod = true;

            do
            {
                var calculationPeriod = Create(periodStartDate, periodEndDate, frequency,
                                               calculationPeriodDatesAdjustments, paymentCalendar, isFirstPeriod);
                isFirstPeriod = false;
                //adjust for roll convention.
                periodStartDate = calculationPeriod.unadjustedStartDate;
                periodEndDate   = calculationPeriod.unadjustedEndDate;
                if (isFinalStub)//Always at least one period.
                {
                    result.FinalStubCalculationPeriod = calculationPeriod;
                    periodEndDate   = periodStartDate;
                    periodStartDate = AddPeriod(periodEndDate, IntervalHelper.FromFrequency(frequency), -1);
                    isFinalStub     = false;
                }
                else if (calculationPeriod.unadjustedStartDate.Date >= startDate.Date)
                {
                    result.Add(calculationPeriod);
                    periodEndDate   = periodStartDate;
                    periodStartDate = AddPeriod(periodEndDate, IntervalHelper.FromFrequency(frequency), -1);
                }
                else if (calculationPeriod.unadjustedStartDate.Date == startDate.Date)//first period - not stub
                {
                    result.Add(calculationPeriod);
                    break;
                }
                else if (calculationPeriod.unadjustedStartDate.Date < startDate.Date)//first period - not stub
                {
                    var number = result.CalculationPeriods.Count;
                    result.CalculationPeriods[number - 1].unadjustedStartDate = startDate.Date;
                    result.CalculationPeriods[number - 1].adjustedStartDate   = startDate.Date;
                    break;
                }
                if (calculationPeriod.unadjustedEndDate.Date <= startDate.Date)//first period - not stub
                {
                    break;
                }
            } while (true);
            if (isInitialStub)//TODO -Fix the unadjustedstartdate which is affected by the roll convention.
            {
                var calculationPeriod = CreateStub(unadjustedStartDate, startDate.Date,
                                                   calculationPeriodDatesAdjustments, paymentCalendar);
                result.InitialStubCalculationPeriod = calculationPeriod;
            }
        }
        public static List <PaymentCalculationPeriod> GetPaymentCalculationPeriods(InterestRateStream interestRateStream,
                                                                                   CalculationPeriodsPrincipalExchangesAndStubs listCalculationPeriods,
                                                                                   IBusinessCalendar paymentCalendar)
        {
            var result = new List <PaymentCalculationPeriod>();
            // Calculate how much calculation periods comprise one payment calculation period
            //
            PaymentDates paymentDates               = interestRateStream.paymentDates;
            Period       paymentFrequency           = IntervalHelper.FromFrequency(paymentDates.paymentFrequency);
            Period       calculationPeriodFrequency = IntervalHelper.FromFrequency(interestRateStream.calculationPeriodDates.calculationPeriodFrequency);

            //  If the payment frequency is less frequent than the frequency defined in the calculation period dates component
            //then more than one calculation period will contribute to e payment amount.
            //  A payment frequency more frequent than the calculation period frequency
            //or one that is not a multiple of the calculation period frequency is invalid.
            if (paymentFrequency.period != calculationPeriodFrequency.period)
            {
                throw new NotSupportedException(
                          $"Payment period type ({paymentFrequency.period}) and calculation period type ({calculationPeriodFrequency.period}) are different. This is not supported.");
            }
            if (0 != int.Parse(paymentFrequency.periodMultiplier) % int.Parse(calculationPeriodFrequency.periodMultiplier))
            {
                throw new NotSupportedException(String.Format("Payment period frequency  is not a multiple of the calculation period frequency. This is not supported."));
            }
            if (int.Parse(paymentFrequency.periodMultiplier) < int.Parse(calculationPeriodFrequency.periodMultiplier))
            {
                throw new NotSupportedException(String.Format("A payment frequency more frequent than the calculation period frequency. This is not supported."));
            }
            int calculationPeriodsInPaymentPeriod = int.Parse(paymentFrequency.periodMultiplier) / int.Parse(calculationPeriodFrequency.periodMultiplier);
            IEnumerable <List <CalculationPeriod> > listOfCalculationPeriodsInSinglePayPeriod = SplitCalculationPeriodsByPaymentPeriods(listCalculationPeriods, calculationPeriodsInPaymentPeriod);
            Offset paymentDaysOffset = paymentDates.paymentDaysOffset;
            BusinessDayAdjustments paymentDatesBusinessDayAdjustments = paymentDates.paymentDatesAdjustments;
            PayRelativeToEnum      payRelativeTo = paymentDates.payRelativeTo;

            //if (paymentCalendar == null)
            //{
            //    paymentCalendar = BusinessCenterHelper.ToBusinessCalendar(cache, paymentDatesBusinessDayAdjustments.businessCenters);
            //}
            // Initial stub
            //
            if (listCalculationPeriods.HasInitialStub)
            {
                var paymentCalculationPeriod = new PaymentCalculationPeriod
                {
                    adjustedPaymentDate =
                        CalculatePaymentDate(paymentDates, payRelativeTo,
                                             new List <CalculationPeriod>(
                                                 new[]
                    {
                        listCalculationPeriods.
                        InitialStubCalculationPeriod
                    }), paymentDaysOffset,
                                             paymentDatesBusinessDayAdjustments, paymentCalendar),
                    adjustedPaymentDateSpecified = true
                };
                XsdClassesFieldResolver.SetPaymentCalculationPeriodCalculationPeriodArray(paymentCalculationPeriod, new[] { listCalculationPeriods.InitialStubCalculationPeriod });
                result.Add(paymentCalculationPeriod);
            }
            //  Regular periods
            //
            foreach (List <CalculationPeriod> calculationPeriodsInPamentPeriod in listOfCalculationPeriodsInSinglePayPeriod)
            {
                var paymentCalculationPeriod = new PaymentCalculationPeriod
                {
                    adjustedPaymentDate =
                        CalculatePaymentDate(paymentDates, payRelativeTo,
                                             calculationPeriodsInPamentPeriod,
                                             paymentDaysOffset,
                                             paymentDatesBusinessDayAdjustments, paymentCalendar),
                    adjustedPaymentDateSpecified = true
                };
                XsdClassesFieldResolver.SetPaymentCalculationPeriodCalculationPeriodArray(paymentCalculationPeriod, calculationPeriodsInPamentPeriod.ToArray());
                result.Add(paymentCalculationPeriod);
            }
            // Final stub
            //
            if (listCalculationPeriods.HasFinalStub)
            {
                var paymentCalculationPeriod = new PaymentCalculationPeriod
                {
                    adjustedPaymentDate =
                        CalculatePaymentDate(paymentDates, payRelativeTo,
                                             new List <CalculationPeriod>(
                                                 new[]
                    {
                        listCalculationPeriods.
                        FinalStubCalculationPeriod
                    }), paymentDaysOffset,
                                             paymentDatesBusinessDayAdjustments, paymentCalendar),
                    adjustedPaymentDateSpecified = true
                };
                XsdClassesFieldResolver.SetPaymentCalculationPeriodCalculationPeriodArray(paymentCalculationPeriod, new[] { listCalculationPeriods.FinalStubCalculationPeriod });
                result.Add(paymentCalculationPeriod);
            }
            return(result);
        }
        public static CalculationPeriodsPrincipalExchangesAndStubs GenerateCalculationPeriodsPrincipalExchangesAndStubs(
            InterestRateStream interestRateStream, IBusinessCalendar fixingCalendar, IBusinessCalendar paymentCalendar)
        {
            CalculationPeriodDates calculationPeriodDates    = interestRateStream.calculationPeriodDates;
            AdjustableDate         adjustableEffectiveDate   = XsdClassesFieldResolver.CalculationPeriodDatesGetEffectiveDate(calculationPeriodDates);
            AdjustableDate         adjustableTerminationDate = XsdClassesFieldResolver.CalculationPeriodDatesGetTerminationDate(calculationPeriodDates);
            AdjustableDate         adjustableFirstPeriodDate = adjustableEffectiveDate;
            DateTime?firstRegularPeriodStartDate             =
                XsdClassesFieldResolver.CalculationPeriodDatesGetFirstRegularPeriodStartDate(calculationPeriodDates);
            var tempDate = XsdClassesFieldResolver.CalculationPeriodDatesGetFirstPeriodStartDate(calculationPeriodDates);

            if (tempDate != null && firstRegularPeriodStartDate != null)
            {
                adjustableFirstPeriodDate = tempDate;
                Frequency frequency = calculationPeriodDates.calculationPeriodFrequency;
                var       startDate = CalculationPeriodGenerator.AddPeriod((DateTime)firstRegularPeriodStartDate, IntervalHelper.FromFrequency(frequency), -1);
                adjustableFirstPeriodDate.unadjustedDate = IdentifiedDateHelper.Create(startDate);
            }
            DateTime?lastRegularPeriodEndDate =
                XsdClassesFieldResolver.CalculationPeriodDatesGetLastRegularPeriodEndDate(calculationPeriodDates);
            //            This assumes automatic adjustment of calculationperiods.
            CalculationPeriodsPrincipalExchangesAndStubs result = CalculationPeriodGenerator.GenerateAdjustedCalculationPeriods(
                adjustableFirstPeriodDate.unadjustedDate.Value,
                adjustableTerminationDate.unadjustedDate.Value,
                firstRegularPeriodStartDate,
                lastRegularPeriodEndDate,
                calculationPeriodDates.calculationPeriodFrequency,
                calculationPeriodDates.calculationPeriodDatesAdjustments,
                paymentCalendar);
            //Determine whether the reset dates must be calcuated.
            Calculation calculation = XsdClassesFieldResolver.CalculationPeriodAmountGetCalculation(interestRateStream.calculationPeriodAmount);

            //  Add principle exchanges if this need is defined in parametric representation of the interest rate steam.
            //
            if (null != interestRateStream.principalExchanges)
            {
                //if (paymentCalendar == null)
                //{
                //    paymentCalendar = BusinessCenterHelper.ToBusinessCalendar(cache, adjustableEffectiveDate.dateAdjustments.businessCenters);
                //}
                //  Initial PE
                //
                if (interestRateStream.principalExchanges.initialExchange)
                {
                    PrincipalExchange initialExchange = PrincipalExchangeHelper.Create(AdjustedDateHelper.ToAdjustedDate(paymentCalendar, adjustableEffectiveDate));
                    result.InitialPrincipalExchange = initialExchange;
                }
                //  intermediatory PE
                //
                if (interestRateStream.principalExchanges.intermediateExchange)
                {
                    // Generate a list of intermediatory PE exchanges
                    //
                    Notional notionalSchedule = XsdClassesFieldResolver.CalculationGetNotionalSchedule(calculation);
                    if (null != notionalSchedule.notionalStepSchedule.step)//there should be steps - otherwise NO interm. exchanges.
                    {
                        foreach (DateTime stepDate in ScheduleHelper.GetStepDates(notionalSchedule.notionalStepSchedule))
                        {
                            PrincipalExchange intermediatoryExchange = PrincipalExchangeHelper.Create(stepDate);
                            result.Add(intermediatoryExchange);
                        }
                    }
                }
                //  Final PE
                // Assume the same calendar is used for the termination date as well!
                if (interestRateStream.principalExchanges.finalExchange)
                {
                    PrincipalExchange finalExchange = PrincipalExchangeHelper.Create(AdjustedDateHelper.ToAdjustedDate(paymentCalendar, adjustableTerminationDate));
                    result.FinalPrincipalExchange = finalExchange;
                }
            }
            //Only does upfront resetRelativeTo start date.
            if (interestRateStream.resetDates != null && calculation.Items[0].GetType() == typeof(FloatingRateCalculation))
            {
                //Get the fixing date convention.
                var fixingDateConvention = interestRateStream.resetDates.resetDatesAdjustments;
                //if (fixingCalendar == null)
                //{
                //    fixingCalendar = BusinessCenterHelper.ToBusinessCalendar(cache, fixingDateConvention.businessCenters);
                //}
                foreach (var calculationPeriod in result.CalculationPeriods)
                {
                    if (calculationPeriod.adjustedStartDateSpecified)
                    {
                        //Set the adjusted fixing date.
                        var adjustedFixingDate = AdjustedDateHelper.ToAdjustedDate(fixingCalendar, calculationPeriod.adjustedStartDate,
                                                                                   fixingDateConvention);
                        var floatingRateDefinition = new FloatingRateDefinition();
                        var rateObservation        = new RateObservation
                        {
                            observedRateSpecified       = false,
                            adjustedFixingDateSpecified = true,
                            adjustedFixingDate          = adjustedFixingDate
                        };
                        floatingRateDefinition.rateObservation = new[] { rateObservation };
                        calculationPeriod.Item1 = floatingRateDefinition;
                    }
                }
                //The initial stub period.
                if (result.InitialStubCalculationPeriod != null)
                {
                    if (result.InitialStubCalculationPeriod.adjustedStartDateSpecified)
                    {
                        //Set the adjusted fixing date.
                        var adjustedFixingDate = AdjustedDateHelper.ToAdjustedDate(fixingCalendar, result.InitialStubCalculationPeriod.adjustedStartDate,
                                                                                   fixingDateConvention);
                        var floatingRateDefinition = new FloatingRateDefinition();
                        var rateObservation        = new RateObservation
                        {
                            observedRateSpecified       = false,
                            adjustedFixingDateSpecified = true,
                            adjustedFixingDate          = adjustedFixingDate
                        };
                        floatingRateDefinition.rateObservation    = new[] { rateObservation };
                        result.InitialStubCalculationPeriod.Item1 = floatingRateDefinition;
                    }
                }
                //The final stub period
                if (result.FinalStubCalculationPeriod != null)
                {
                    if (result.FinalStubCalculationPeriod.adjustedStartDateSpecified)
                    {
                        //Set the adjusted fixing date.
                        var adjustedFixingDate = AdjustedDateHelper.ToAdjustedDate(fixingCalendar, result.FinalStubCalculationPeriod.adjustedStartDate,
                                                                                   fixingDateConvention);
                        var floatingRateDefinition = new FloatingRateDefinition();
                        var rateObservation        = new RateObservation
                        {
                            observedRateSpecified       = false,
                            adjustedFixingDateSpecified = true,
                            adjustedFixingDate          = adjustedFixingDate
                        };
                        floatingRateDefinition.rateObservation  = new[] { rateObservation };
                        result.FinalStubCalculationPeriod.Item1 = floatingRateDefinition;
                    }
                }
            }
            return(result);
        }