/// <summary>
        ///
        /// </summary>
        /// <param name="payerIsBase"></param>
        /// <param name="calculation"></param>
        /// <param name="coupon"></param>
        /// <param name="fOCalculationMethod"></param>
        /// <param name="fixingCalendar"></param>
        /// <param name="paymentCalendar"></param>
        /// <returns></returns>
        public static PriceableRateCoupon CreatePriceableCoupon(bool payerIsBase, Calculation calculation, PaymentCalculationPeriod coupon, bool fOCalculationMethod
                                                                , IBusinessCalendar fixingCalendar, IBusinessCalendar paymentCalendar)
        {
            var notional         = (Notional)calculation.Item;
            var currency         = notional.notionalStepSchedule.currency;
            var dayCountFraction = calculation.dayCountFraction;
            var paymentDate      = coupon.adjustedPaymentDateSpecified
                                  ? coupon.adjustedPaymentDate
                                  : coupon.unadjustedPaymentDate;
            var calculationPeriods      = XsdClassesFieldResolver.GetPaymentCalculationPeriodCalculationPeriodArray(coupon);
            var discountFactorSpecified = coupon.discountFactorSpecified;

            if (calculationPeriods.Length == 1)
            {
                var calculationPeriod = calculationPeriods[0];
                var notionalAmount    = XsdClassesFieldResolver.CalculationPeriodGetNotionalAmount(calculationPeriod);
                var money             = payerIsBase ? MoneyHelper.GetAmount(-1 * notionalAmount, currency) : MoneyHelper.GetAmount(notionalAmount, currency);
                var accrualStartDate  = calculationPeriod.adjustedStartDateSpecified
                                           ? calculationPeriod.adjustedStartDate
                                           : calculationPeriod.unadjustedStartDate;
                var accrualEndDate = calculationPeriod.adjustedEndDateSpecified
                                         ? calculationPeriod.adjustedEndDate
                                         : calculationPeriod.unadjustedEndDate;
                //  If has a fixed rate (fixed rate coupon)
                var isThereDiscounting = XsdClassesFieldResolver.CalculationHasDiscounting(calculation);
                PriceableRateCoupon rateCoupon;
                if (XsdClassesFieldResolver.CalculationPeriodHasFixedRate(calculationPeriod))
                {
                    decimal finalRate = XsdClassesFieldResolver.CalculationPeriodGetFixedRate(calculationPeriod);
                    //The discount rate must be set. It would normally be the final rate. The assumption is that, it the discounting rate is zero then the final rate should be used.
                    if (isThereDiscounting)
                    {
                        var discounting = XsdClassesFieldResolver.CalculationGetDiscounting(calculation);
                        //This test works because if the rate is zero, then the coupon is not discounted and discounting Type should be null.
                        var discountRate = discounting.discountRate == 0.0m ? finalRate : discounting.discountRate;
                        rateCoupon = new PriceableFixedRateCoupon(coupon.id, payerIsBase, accrualStartDate, accrualEndDate,
                                                                  dayCountFraction, finalRate, money, null, paymentDate, discounting.discountingType, discountRate,
                                                                  null, paymentCalendar);
                        if (discountFactorSpecified)
                        {
                            rateCoupon.PaymentDiscountFactor = coupon.discountFactor;
                        }
                        return(rateCoupon);
                    }
                    rateCoupon = new PriceableFixedRateCoupon(coupon.id, payerIsBase, accrualStartDate, accrualEndDate,
                                                              dayCountFraction, finalRate, money, null, paymentDate, null, null,
                                                              null, paymentCalendar);
                    if (discountFactorSpecified)
                    {
                        rateCoupon.PaymentDiscountFactor = coupon.discountFactor;
                    }
                    return(rateCoupon);
                }
                if (XsdClassesFieldResolver.CalculationPeriodHasFloatingRateDefinition(calculationPeriod))
                {
                    //The floating rate definition.
                    FloatingRateDefinition floatingRateDefinition = XsdClassesFieldResolver.CalculationPeriodGetFloatingRateDefinition(calculationPeriod);
                    //The floating rate Calculation.
                    Debug.Assert(calculation.Items != null);
                    Debug.Assert(calculation.Items.Length > 0);
                    Debug.Assert(calculation.Items[0] is FloatingRateCalculation);
                    var floatingRateCalculation = (FloatingRateCalculation)calculation.Items[0];
                    //The forecast rate index.
                    var floatingRateIndex = floatingRateCalculation.floatingRateIndex;
                    var indexTenor        = floatingRateCalculation.indexTenor.ToString();
                    var forecastRate      = ForecastRateIndexHelper.Parse(floatingRateIndex.Value, indexTenor);
                    //The rate observation
                    // Apply spread from schedule if it hasn't been specified yet.
                    var margin = 0m;
                    if (floatingRateDefinition.spreadSpecified)
                    {
                        margin = floatingRateDefinition.spread;
                    }
                    //The observed rate.
                    Decimal?observedRate = null;
                    Decimal?capStrike    = null;
                    Decimal?floorStrike  = null;
                    if (floatingRateDefinition.capRate != null)
                    {
                        capStrike = floatingRateDefinition.capRate[0].strikeRate;
                    }
                    if (floatingRateDefinition.floorRate != null)
                    {
                        floorStrike = floatingRateDefinition.floorRate[0].strikeRate;
                    }
                    if (floatingRateDefinition.rateObservation != null)//TODO This is a big problem. Need to handle the case of no fixing date!
                    {
                        var rateObservation = floatingRateDefinition.rateObservation[0];
                        if (rateObservation.observedRateSpecified)
                        {
                            observedRate = rateObservation.observedRate;
                        }
                        //Removed because Igor's old code populates these fields when the trade is created. This means the coupon is not recalculated!
                        //Now the coupon will ignore any previous calculations and only treat as a fixed coupon if the observed rate has been specified.
                        if (isThereDiscounting)
                        {
                            var discounting = XsdClassesFieldResolver.CalculationGetDiscounting(calculation);
                            if (capStrike != null || floorStrike != null)
                            {
                                rateCoupon = new PriceableCapFloorCoupon(coupon.id, !payerIsBase,
                                                                         capStrike, floorStrike, accrualStartDate, accrualEndDate,
                                                                         rateObservation.adjustedFixingDate, dayCountFraction,
                                                                         margin, observedRate, money, paymentDate,
                                                                         forecastRate, discounting.discountingType,
                                                                         observedRate, null, fixingCalendar, paymentCalendar);
                            }
                            else
                            {
                                rateCoupon = new PriceableFloatingRateCoupon(coupon.id, !payerIsBase, accrualStartDate, accrualEndDate,
                                                                             rateObservation.adjustedFixingDate, dayCountFraction,
                                                                             margin, observedRate, money, paymentDate,
                                                                             forecastRate, discounting.discountingType,
                                                                             observedRate, null, fixingCalendar, paymentCalendar);
                            }
                        }
                        else
                        {
                            if (capStrike != null || floorStrike != null)
                            {
                                rateCoupon = new PriceableCapFloorCoupon(coupon.id, !payerIsBase, capStrike, floorStrike,
                                                                         accrualStartDate, accrualEndDate,
                                                                         rateObservation.adjustedFixingDate, dayCountFraction,
                                                                         margin, observedRate, money, paymentDate,
                                                                         forecastRate, null, null, null,
                                                                         fixingCalendar, paymentCalendar);
                            }
                            else
                            {
                                rateCoupon = new PriceableFloatingRateCoupon(coupon.id, payerIsBase, accrualStartDate, accrualEndDate,
                                                                             rateObservation.adjustedFixingDate, dayCountFraction,
                                                                             margin, observedRate, money, paymentDate,
                                                                             forecastRate, null, null, null,
                                                                             fixingCalendar, paymentCalendar);
                            }
                        }
                        if (fOCalculationMethod)
                        {
                            ((PriceableFloatingRateCoupon)rateCoupon).ForecastRateInterpolation = true;
                        }
                        if (discountFactorSpecified)
                        {
                            rateCoupon.PaymentDiscountFactor = coupon.discountFactor;
                        }
                        return(rateCoupon);
                    }
                    throw new NotImplementedException("Need to return a rate coupon, Alex!");
                }
                throw new System.Exception("CalculationPeriod has neither fixedRate nor floatingRateDefinition.");
            }
            throw new System.Exception("PaymentCalculationPeriod has zero, or multiple CalculationPeriods.");
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="buyerIsBase"></param>
        /// <param name="calculation"></param>
        /// <param name="coupon"></param>
        /// <param name="fOCalculationMethod"></param>
        /// <param name="fixingCalendar"></param>
        /// <param name="paymentCalendar"></param>
        /// <returns></returns>
        public static PriceableCapFloorCoupon CreatePriceableCapFloorCoupon(bool buyerIsBase,
                                                                            Calculation calculation, PaymentCalculationPeriod coupon, bool fOCalculationMethod
                                                                            , IBusinessCalendar fixingCalendar, IBusinessCalendar paymentCalendar)
        {
            var notional         = (Notional)calculation.Item;
            var currency         = notional.notionalStepSchedule.currency;
            var dayCountFraction = calculation.dayCountFraction;
            var paymentDate      = coupon.adjustedPaymentDateSpecified
                                  ? coupon.adjustedPaymentDate
                                  : coupon.unadjustedPaymentDate;
            var calculationPeriods =
                XsdClassesFieldResolver.GetPaymentCalculationPeriodCalculationPeriodArray(coupon);

            if (calculationPeriods.Length == 1)
            {
                var calculationPeriod = calculationPeriods[0];
                //Money expectedCashFlow = null;
                decimal notionalamount   = XsdClassesFieldResolver.CalculationPeriodGetNotionalAmount(calculationPeriod);
                Money   money            = buyerIsBase ? MoneyHelper.GetAmount(-1 * notionalamount, currency) : MoneyHelper.GetAmount(notionalamount, currency);
                var     accrualStartDate = calculationPeriod.adjustedStartDateSpecified
                                           ? calculationPeriod.adjustedStartDate
                                           : calculationPeriod.unadjustedStartDate;
                var accrualEndDate = calculationPeriod.adjustedEndDateSpecified
                                         ? calculationPeriod.adjustedEndDate
                                         : calculationPeriod.unadjustedEndDate;
                //  If has a fixed rate (fixed rate coupon)
                var isThereDiscounting = XsdClassesFieldResolver.CalculationHasDiscounting(calculation);
                if (XsdClassesFieldResolver.CalculationPeriodHasFloatingRateDefinition(calculationPeriod))
                {
                    //The floating rate definition.
                    FloatingRateDefinition floatingRateDefinition = XsdClassesFieldResolver.CalculationPeriodGetFloatingRateDefinition(calculationPeriod);
                    //The floatingrateCalculation.
                    Debug.Assert(calculation.Items != null);
                    Debug.Assert(calculation.Items.Length > 0);
                    Debug.Assert(calculation.Items[0] is FloatingRateCalculation);
                    var floatingRateCalculation = (FloatingRateCalculation)calculation.Items[0];
                    //The forecast rate index.
                    var floatingRateIndex = floatingRateCalculation.floatingRateIndex;
                    var indexTenor        = floatingRateCalculation.indexTenor.ToString();
                    var forecastRate      = ForecastRateIndexHelper.Parse(floatingRateIndex.Value, indexTenor);
                    //The rate observation
                    // Apply spread from schedule if it hasn't been specified yet.
                    decimal margin = 0m;
                    if (floatingRateDefinition.spreadSpecified)
                    {
                        margin = floatingRateDefinition.spread;
                    }
                    //The observed rate.
                    Decimal?observedRate = null;
                    Decimal?capStrike    = null;
                    Decimal?floorStrike  = null;
                    if (floatingRateDefinition.capRate != null)
                    {
                        capStrike = floatingRateDefinition.capRate[0].strikeRate;
                    }
                    if (floatingRateDefinition.floorRate != null)
                    {
                        floorStrike = floatingRateDefinition.floorRate[0].strikeRate;
                    }
                    if (floatingRateDefinition.rateObservation != null)
                    {
                        var rateObservation = floatingRateDefinition.rateObservation[0];
                        if (rateObservation.observedRateSpecified)
                        {
                            observedRate = rateObservation.observedRate;
                        }
                        PriceableCapFloorCoupon rateCoupon;
                        if (isThereDiscounting)
                        {
                            var discounting = XsdClassesFieldResolver.CalculationGetDiscounting(calculation);
                            var floatingCouponWithDiscounting = new PriceableCapFloorCoupon(coupon.id, buyerIsBase,
                                                                                            capStrike, floorStrike, accrualStartDate, accrualEndDate,
                                                                                            rateObservation.adjustedFixingDate, dayCountFraction,
                                                                                            margin, observedRate, money, paymentDate,
                                                                                            forecastRate, discounting.discountingType,
                                                                                            observedRate, null, fixingCalendar, paymentCalendar);
                            if (fOCalculationMethod)
                            {
                                floatingCouponWithDiscounting.ForecastRateInterpolation = true;
                            }
                            rateCoupon = floatingCouponWithDiscounting;
                            return(rateCoupon);
                        }
                        var floatingCoupon = new PriceableCapFloorCoupon(coupon.id, buyerIsBase, capStrike, floorStrike,
                                                                         accrualStartDate, accrualEndDate,
                                                                         rateObservation.adjustedFixingDate, dayCountFraction,
                                                                         margin, observedRate, money, paymentDate,
                                                                         forecastRate, null, null, null,
                                                                         fixingCalendar, paymentCalendar);
                        if (fOCalculationMethod)
                        {
                            floatingCoupon.ForecastRateInterpolation = true;
                        }
                        rateCoupon = floatingCoupon;
                        return(rateCoupon);
                    }
                    throw new NotImplementedException("Need to return a rate coupon, Alex!");
                }
                throw new System.Exception("CalculationPeriod has neither fixedRate nor floatingRateDefinition.");
            }
            throw new System.Exception("PaymentCalculationPeriod has zero, or multiple CalculationPeriods.");
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="PriceableInterestRateStream"/> class.
        /// </summary>
        /// <param name="logger">The logger.</param>
        /// <param name="cache">The cache.</param>
        /// <param name="nameSpace">The client namespace.</param>
        /// <param name="swapId">The swap Id.</param>
        /// <param name="payerPartyReference">The payer party reference.</param>
        /// <param name="receiverPartyReference">The receiver party reference.</param>
        /// <param name="payerIsBase">The flag for whether the payer reference is the base party.</param>
        /// <param name="calculationPeriodDates">The calculation period date information.</param>
        /// <param name="paymentDates">The payment dates of the swap leg.</param>
        /// <param name="resetDates">The reset dates of the swap leg.</param>
        /// <param name="principalExchanges">The principal Exchange type.</param>
        /// <param name="calculationPeriodAmount">The calculation period amount data.</param>
        /// <param name="stubCalculationPeriodAmount">The stub calculation information.</param>
        /// <param name="cashflows">The FpML cashflows for that stream.</param>
        /// <param name="settlementProvision">The settlement provision data.</param>
        /// <param name="forecastRateInterpolation">ForwardEndDate = forecastRateInterpolation ? AccrualEndDate
        /// : AdjustedDateHelper.ToAdjustedDate(forecastRateIndex.indexTenor.Add(AccrualStartDate), AccrualBusinessDayAdjustments);</param>
        /// <param name="fixingCalendar">The fixingCalendar.</param>
        /// <param name="paymentCalendar">The paymentCalendar.</param>
        public PriceableInterestRateStream
        (
            ILogger logger
            , ICoreCache cache
            , String nameSpace
            , string swapId
            , string payerPartyReference
            , string receiverPartyReference
            , bool payerIsBase
            , CalculationPeriodDates calculationPeriodDates
            , PaymentDates paymentDates
            , ResetDates resetDates
            , PrincipalExchanges principalExchanges
            , CalculationPeriodAmount calculationPeriodAmount
            , StubCalculationPeriodAmount stubCalculationPeriodAmount
            , Cashflows cashflows
            , SettlementProvision settlementProvision
            , bool forecastRateInterpolation
            , IBusinessCalendar fixingCalendar
            , IBusinessCalendar paymentCalendar)
        {
            Multiplier              = 1.0m;
            Payer                   = payerPartyReference;
            Receiver                = receiverPartyReference;
            PayerIsBaseParty        = payerIsBase;
            CalculationPeriodDates  = calculationPeriodDates;
            PaymentDates            = paymentDates;
            PaymentCurrencies       = new List <string>();
            ResetDates              = resetDates;
            PrincipalExchanges      = principalExchanges;
            CalculationPeriodAmount = calculationPeriodAmount;
            AnalyticsModel          = new StructuredStreamAnalytic();
            Calculation             = (Calculation)CalculationPeriodAmount.Item;
            if (Calculation.Items?[0] is Schedule strikeSchedule)
            {
                Strike = strikeSchedule.initialValue;//Only picks up the first fixed rate for the swaption calculation.
            }
            StubCalculationPeriodAmount = stubCalculationPeriodAmount;
            Cashflows        = cashflows;
            CouponStreamType = CouponTypeFromCalculation(Calculation);
            Id = BuildId(swapId, CouponStreamType);
            ForecastRateInterpolation = forecastRateInterpolation;
            var isThereDiscounting = XsdClassesFieldResolver.CalculationHasDiscounting(Calculation);

            if (isThereDiscounting)
            {
                IsDiscounted = true; //TODO need to include rate logic for the correct solved answers. What about reset cashflows??
            }
            //Get the currency.
            var currency = XsdClassesFieldResolver.CalculationGetNotionalSchedule(Calculation);

            Currency = currency.notionalStepSchedule.currency;
            if (!PaymentCurrencies.Contains(Currency.Value))
            {
                PaymentCurrencies.Add(Currency.Value);
            }
            //The calendars
            if (paymentCalendar == null)
            {
                paymentCalendar = BusinessCenterHelper.ToBusinessCalendar(cache, PaymentDates.paymentDatesAdjustments.businessCenters, nameSpace);
            }
            SettlementProvision = settlementProvision;
            //Set the default discount curve name.
            DiscountCurveName = CurveNameHelpers.GetDiscountCurveName(Currency.Value, true);
            //Set the forecast curve name.//TODO extend this to the other types.
            if (CouponStreamType != CouponStreamType.GenericFixedRate)
            {
                if (fixingCalendar == null)
                {
                    fixingCalendar = BusinessCenterHelper.ToBusinessCalendar(cache, ResetDates.resetDatesAdjustments.businessCenters, nameSpace);
                }
                ForecastCurveName = null;
                if (Calculation.Items != null)
                {
                    var floatingRateCalculation = Calculation.Items;
                    var floatingRateIndex       = (FloatingRateCalculation)floatingRateCalculation[0];
                    ForecastCurveName = CurveNameHelpers.GetForecastCurveName(floatingRateIndex);
                }
            }
            //Build the coupons and principal exchanges.
            if (GetCashflowPaymentCalculationPeriods() != null)
            {
                Coupons = PriceableInstrumentsFactory.CreatePriceableCoupons(PayerIsBaseParty,
                                                                             GetCashflowPaymentCalculationPeriods(),
                                                                             Calculation, ForecastRateInterpolation, fixingCalendar, paymentCalendar);//TODO add the stubcalculation.
                UpdateCouponIds();
            }
            if (GetCashflowPrincipalExchanges() != null)
            {
                var exchanges = GetCashflowPrincipalExchanges();
                Exchanges = PriceableInstrumentsFactory.CreatePriceablePrincipalExchanges(PayerIsBaseParty, exchanges, Currency.Value, paymentCalendar);
                UpdateExchangeIds();
            }
            RiskMaturityDate = LastDate();
            logger.LogInfo("Stream built");
        }