/// <summary>
        /// Prepare for valuation.
        /// </summary>
        public override void PreValue(PriceFactorList factors)
        {
            base.PreValue(factors);

            CFGeneralInterestSpreadListDeal deal = (CFGeneralInterestSpreadListDeal)Deal;

            // Get spread flow characteristics
            SpreadCashflowListCharacteristics spreadCashflowCharacteristics = fCashflows.ValuationPriceFactorDependencies(factors.BaseDate, fCurrency, fForecastCurrency, fForecast2Currency);

            // vols for first forecast rate
            if (spreadCashflowCharacteristics.NeedForecast1YieldVol)
            {
                fForecast1YieldVol = InterestVolBase.GetYieldVol(factors, deal.Forecast_Rate1_Swaption_Volatility, fForecastCurrency);
            }

            if (spreadCashflowCharacteristics.NeedForecast1RateVol)
            {
                fForecast1RateVol = InterestVolBase.GetRateVol(factors, deal.Forecast_Rate1_Cap_Volatility, fForecastCurrency);
            }

            // vols for second forecast rate
            if (spreadCashflowCharacteristics.NeedForecast2YieldVol)
            {
                fForecast2YieldVol = InterestVolBase.GetYieldVol(factors, deal.Forecast_Rate2_Swaption_Volatility, fForecast2Currency);
            }

            if (spreadCashflowCharacteristics.NeedForecast2RateVol)
            {
                fForecast2RateVol = InterestVolBase.GetRateVol(factors, deal.Forecast_Rate2_Cap_Volatility, fForecast2Currency);
            }

            // vols for discount rate
            if (spreadCashflowCharacteristics.NeedDiscountYieldVol)
            {
                fDiscountYieldVol = InterestVolBase.GetYieldVol(factors, deal.Discount_Rate_Swaption_Volatility, fCurrency);
            }

            if (spreadCashflowCharacteristics.NeedDiscountRateVol)
            {
                fDiscountRateVol = InterestVolBase.GetRateVol(factors, deal.Discount_Rate_Cap_Volatility, fCurrency);
            }

            bool convexity = spreadCashflowCharacteristics.NeedDiscountYieldVol || spreadCashflowCharacteristics.NeedDiscountRateVol;

            if (fForecastCurrency != fCurrency)
            {
                if (Quanto_Correction == YesNo.Yes)
                {
                    // fx vol, fx/ir correl and forecast/discount correl
                    fFx1Vol             = FXVolHelper.Get(factors, fForecastCurrency, fCurrency);
                    fForecast1Fx1Correl = CorrelationHelper.Get(factors, typeof(InterestRate), fForecastCurrency, null, typeof(FxRate), fForecastCurrency, fCurrency);
                }

                if (convexity)
                {
                    fForecast1DiscountCorrel = CorrelationHelper.Get(factors, typeof(InterestRate), fCurrency, null, typeof(InterestRate), fForecastCurrency, null);
                }
            }

            if (fForecast2Currency != fCurrency)
            {
                if (Quanto_Correction == YesNo.Yes)
                {
                    // fx vol, fx/ir correl and forecast/discount correl
                    fFx2Vol             = FXVolHelper.Get(factors, fForecast2Currency, fCurrency);
                    fForecast2Fx2Correl = CorrelationHelper.Get(factors, typeof(InterestRate), fForecast2Currency, null, typeof(FxRate), fForecast2Currency, fCurrency);
                }

                if (convexity)
                {
                    fForecast2DiscountCorrel = CorrelationHelper.Get(factors, typeof(InterestRate), fCurrency, null, typeof(InterestRate), fForecast2Currency, null);
                }
            }

            if (spreadCashflowCharacteristics.NeedForecast1Forecast2Correlation)
            {
                if (fForecastCurrency == fForecast2Currency)
                {
                    // correl between forecast rates in same currency
                    fForecast1Forecast2Correls = factors.Get <CMSRateCorrelations>(fForecastCurrency);
                }
                else
                {
                    fForecast1Forecast2Correl = CorrelationHelper.Get(factors, typeof(InterestRate), fForecastCurrency, null, typeof(InterestRate), fForecast2Currency, null);
                }
            }
        }
        /// <summary>
        /// Warn of unnecessary volatility surface definitions.
        /// </summary>
        /// <remarks>
        /// Test differently if the cap and swaption volatility definitions are the same or
        /// distinct because they may have been set by a single property or two distinct ones.
        /// </remarks>
        private static void ValidateUnnecessaryVolatilities(CFGeneralInterestSpreadListDeal deal, SpreadCashflowListCharacteristics characteristics, ErrorList errors)
        {
            if (deal.Discount_Rate_Cap_Volatility == deal.Discount_Rate_Swaption_Volatility)
            {
                if (!characteristics.NeedDiscountRateVol && !characteristics.NeedDiscountYieldVol && !string.IsNullOrEmpty(deal.Discount_Rate_Cap_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Discount_Rate_Cap_Volatility)} and {nameof(deal.Discount_Rate_Swaption_Volatility)} {deal.Discount_Rate_Cap_Volatility}");
                }
            }
            else
            {
                if (!characteristics.NeedDiscountRateVol && !string.IsNullOrEmpty(deal.Discount_Rate_Cap_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Discount_Rate_Cap_Volatility)} {deal.Discount_Rate_Cap_Volatility}");
                }

                if (!characteristics.NeedDiscountYieldVol && !string.IsNullOrEmpty(deal.Discount_Rate_Swaption_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Discount_Rate_Swaption_Volatility)} {deal.Discount_Rate_Swaption_Volatility}");
                }
            }

            if (deal.Forecast_Rate1_Cap_Volatility == deal.Forecast_Rate1_Swaption_Volatility)
            {
                if (!characteristics.NeedForecast1RateVol && !characteristics.NeedForecast1YieldVol && !string.IsNullOrEmpty(deal.Forecast_Rate1_Cap_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Forecast_Rate1_Cap_Volatility)} and {nameof(deal.Forecast_Rate1_Swaption_Volatility)} {deal.Forecast_Rate1_Cap_Volatility}.");
                }
            }
            else
            {
                if (!characteristics.NeedForecast1RateVol && !string.IsNullOrEmpty(deal.Forecast_Rate1_Cap_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Forecast_Rate1_Cap_Volatility)} {deal.Forecast_Rate1_Cap_Volatility}.");
                }

                if (!characteristics.NeedForecast1YieldVol && !string.IsNullOrEmpty(deal.Forecast_Rate1_Swaption_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Forecast_Rate1_Swaption_Volatility)} {deal.Forecast_Rate1_Swaption_Volatility}.");
                }
            }

            if (deal.Forecast_Rate2_Cap_Volatility == deal.Forecast_Rate2_Swaption_Volatility)
            {
                if (!characteristics.NeedForecast2RateVol && !characteristics.NeedForecast2YieldVol && !string.IsNullOrEmpty(deal.Forecast_Rate2_Cap_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Forecast_Rate2_Cap_Volatility)} and {nameof(deal.Forecast_Rate2_Swaption_Volatility)} {deal.Forecast_Rate2_Cap_Volatility}.");
                }
            }
            else
            {
                if (!characteristics.NeedForecast2RateVol && !string.IsNullOrEmpty(deal.Forecast_Rate2_Cap_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Forecast_Rate2_Cap_Volatility)} {deal.Forecast_Rate2_Cap_Volatility}.");
                }

                if (!characteristics.NeedForecast2YieldVol && !string.IsNullOrEmpty(deal.Forecast_Rate2_Swaption_Volatility))
                {
                    deal.AddToErrors(errors, ErrorLevel.Info, $"Unnecessary {nameof(deal.Forecast_Rate2_Swaption_Volatility)} {deal.Forecast_Rate2_Swaption_Volatility}.");
                }
            }
        }
        /// <summary>
        /// Register price factors.
        /// </summary>
        public override void RegisterFactors(PriceFactorList factors, ErrorList errors)
        {
            base.RegisterFactors(factors, errors);

            CFGeneralInterestSpreadListDeal deal = (CFGeneralInterestSpreadListDeal)Deal;

            // Collect registered volatility price factors to check they have the same distribution type
            var volPriceFactors = new List <IInterestVol>();

            // Get spread flow characteristics
            SpreadCashflowListCharacteristics spreadCashflowCharacteristics = deal.Cashflows.ValuationPriceFactorDependencies(factors.BaseDate, fCurrency, fForecastCurrency, fForecast2Currency);

            // register volatility surfaces for forecast rate1
            if (spreadCashflowCharacteristics.NeedForecast1YieldVol)
            {
                volPriceFactors.Add(InterestVolBase.RegisterInterestYieldVol(factors, deal.Forecast_Rate1_Swaption_Volatility, fForecastCurrency));
            }

            if (spreadCashflowCharacteristics.NeedForecast1RateVol)
            {
                volPriceFactors.Add(InterestVolBase.RegisterInterestRateVol(factors, deal.Forecast_Rate1_Cap_Volatility, fForecastCurrency));
            }

            // register volatility surfaces for forecast rate2
            if (spreadCashflowCharacteristics.NeedForecast2YieldVol)
            {
                volPriceFactors.Add(InterestVolBase.RegisterInterestYieldVol(factors, deal.Forecast_Rate2_Swaption_Volatility, fForecast2Currency));
            }

            if (spreadCashflowCharacteristics.NeedForecast2RateVol)
            {
                volPriceFactors.Add(InterestVolBase.RegisterInterestRateVol(factors, deal.Forecast_Rate2_Cap_Volatility, fForecast2Currency));
            }

            // vol surfaces for discount rate
            if (spreadCashflowCharacteristics.NeedDiscountYieldVol)
            {
                volPriceFactors.Add(InterestVolBase.RegisterInterestYieldVol(factors, deal.Discount_Rate_Swaption_Volatility, fCurrency));
            }

            if (spreadCashflowCharacteristics.NeedDiscountRateVol)
            {
                volPriceFactors.Add(InterestVolBase.RegisterInterestRateVol(factors, deal.Discount_Rate_Cap_Volatility, fCurrency));
            }

            bool convexity = spreadCashflowCharacteristics.NeedDiscountYieldVol || spreadCashflowCharacteristics.NeedDiscountRateVol;

            if (fForecastCurrency != fCurrency)
            {
                if (Quanto_Correction == YesNo.Yes)
                {
                    // fx vol, fx/ir correl and forecast/discount correl
                    FXVolHelper.Register(factors, fForecastCurrency, fCurrency);
                    CorrelationHelper.Register(factors, typeof(IInterestRate), fForecastCurrency, null, typeof(IFxRate), fForecastCurrency, fCurrency);
                }

                if (convexity)
                {
                    CorrelationHelper.Register(factors, typeof(IInterestRate), fCurrency, null, typeof(IInterestRate), fForecastCurrency, null);
                }
            }

            if (fForecast2Currency != fCurrency)
            {
                if (Quanto_Correction == YesNo.Yes)
                {
                    // fx vol, fx/ir correl and forecast/discount correl
                    FXVolHelper.Register(factors, fForecast2Currency, fCurrency);
                    CorrelationHelper.Register(factors, typeof(IInterestRate), fForecast2Currency, null, typeof(IFxRate), fForecast2Currency, fCurrency);
                }

                if (convexity)
                {
                    CorrelationHelper.Register(factors, typeof(IInterestRate), fCurrency, null, typeof(IInterestRate), fForecast2Currency, null);
                }
            }

            if (spreadCashflowCharacteristics.NeedForecast1Forecast2Correlation)
            {
                if (fForecastCurrency == fForecast2Currency)
                {
                    // correl between forecast rates in same currency
                    factors.Register <CMSRateCorrelations>(fForecastCurrency);
                }
                else
                {
                    CorrelationHelper.Register(factors, typeof(IInterestRate), fForecastCurrency, null, typeof(IInterestRate), fForecast2Currency, null);
                }
            }

            if (volPriceFactors.Select(pf => pf.GetDistributionType()).Distinct().Count() > 1)
            {
                Deal.AddToErrors(errors, "Volatility price factors must have the same distribution type.");
            }

            ValidateUnnecessaryVolatilities(deal, spreadCashflowCharacteristics, errors);
        }