Example #1
0
        /// <summary>
        /// Value the deal from given base date, price factors and time grid.
        /// </summary>
        public void Value(PVProfiles pvResults, CashAccumulators cashResults, double baseDate, IInterestRate discountRate, IInterestRate forecastRate1, IInterestRate forecastRate2, IFxRate fxRate, TimeGrid timeGrid, int numScenarios)
        {
            var tgi  = new TimeGridIterator(timeGrid);
            var deal = (FloatingInterestCashflowInterpolatedDeal)Deal;

            bool   hasRate1          = deal.HasRate1();
            bool   hasRate2          = deal.HasRate2();
            double scale             = deal.Buy_Sell == BuySell.Buy ? +deal.Principal : -deal.Principal;
            double tPay              = CalcUtils.DaysToYears(fPaymentDate - baseDate);
            double tReset            = CalcUtils.DaysToYears(deal.Reset_Date - baseDate);
            double tRateStart        = CalcUtils.DaysToYears(deal.Rate_Start_Date - baseDate);
            double tRateEnd1         = hasRate1 ? CalcUtils.DaysToYears(fRate1EndDate - baseDate) : 0.0;
            double tRateEnd2         = hasRate2 ? CalcUtils.DaysToYears(fRate2EndDate - baseDate) : 0.0;
            double tRateEnd12        = tRateEnd2 - tRateEnd1;                                                                    // Time from rate 1 end date to rate 2 end date.
            double tAccrualEnd       = CalcUtils.DaysToYears(deal.Accrual_End_Date - baseDate);
            double interpCoefficient = Math.Abs(tRateEnd12) >= CalcUtils.MinTime ? (tAccrualEnd - tRateEnd1) / tRateEnd12 : 0.0; // Coefficient used to calculate interpolated rate.

            VectorEngine.For(tgi, () =>
            {
                using (var cache = Vector.Cache(numScenarios))
                {
                    Vector pv = cache.Get();

                    if (tgi.Date <= fPaymentDate && fPaymentDate > fCutoffDate)
                    {
                        Vector interpRate = cache.GetClear();
                        Vector rate1      = cache.GetClear();
                        Vector rate2      = cache.GetClear();

                        if (hasRate1)
                        {
                            if (fKnownResetRate1.HasValue)
                            {
                                rate1.Assign(fKnownResetRate1.Value);
                            }
                            else
                            {
                                InterestRateUtils.LiborRate(rate1, forecastRate1, tgi.T, tReset, tRateStart, tRateEnd1, fRate1YearFraction);
                            }
                        }

                        if (hasRate2)
                        {
                            if (fKnownResetRate2.HasValue)
                            {
                                rate2.Assign(fKnownResetRate2.Value);
                            }
                            else
                            {
                                InterestRateUtils.LiborRate(rate2, forecastRate2, tgi.T, tReset, tRateStart, tRateEnd2, fRate2YearFraction);
                            }
                        }

                        if (hasRate1 && hasRate2)
                        {
                            if (Math.Abs(tRateEnd12) >= CalcUtils.MinTime)
                            {
                                interpRate.Assign(rate1 + interpCoefficient * (rate2 - rate1));
                            }
                            else
                            {
                                interpRate.Assign(0.5 * rate1 + 0.5 * rate2);
                            }
                        }
                        else
                        {
                            interpRate.Assign(hasRate1 ? rate1 : rate2);
                        }

                        // Round the calculated rate, regardless whether the valuation date is before or after the reset date.
                        CFFloatingInterestList.RoundRateTo(deal.Interpolated_Rate_Rounding, interpRate);

                        pv.Assign(scale * (interpRate + deal.Margin) * fAccrualYearFraction);

                        CFFixedList.RoundCashflow(pv, Cashflow_Rounding);

                        if (tgi.Date < fPaymentDate)
                        {
                            pv.MultiplyBy(discountRate.Get(tgi.T, tPay));
                        }
                        else if (tgi.Date == fPaymentDate)
                        {
                            cashResults.Accumulate(fxRate, fPaymentDate, pv);
                        }
                    }
                    else
                    {
                        pv.Clear();
                    }

                    pvResults.AppendVector(tgi.Date, pv * fxRate.Get(tgi.T));
                }
            });

            // After maturity
            pvResults.Complete(timeGrid);
        }
Example #2
0
        /// <summary>
        /// Value a caplet or floorlet under the 1 factor Hull-White model.
        /// </summary>
        public override void Value(Vector pv, Vector cash, double baseDate, double valueDate, ISACCRResult saccrResult,
                                   IIntraValuationDiagnosticsWriter intraValuationDiagnosticsWriter)
        {
            int count = fCashflows.Count();

            bool forecastIsDiscount = ReferenceEquals(fForecastRate, fDiscountRate);

            // time of dfStart and dfEnd
            double tDfStart = double.NegativeInfinity;
            double tDfEnd   = double.NegativeInfinity;

            using (var cache = Vector.CacheLike(pv))
            {
                // Shared between loops
                Vector dfStart = cache.Get();
                Vector dfEnd   = cache.Get();

                VectorEngine.For(0, count, LoopDirection.Backwards, i =>
                {
                    using (var innerCache = Vector.CacheLike(pv))
                    {
                        CFFloatingInterest cashflow = fCashflows[i];

                        if (cashflow.Payment_Date < valueDate || cashflow.Payment_Date <= fCutoffDate)
                        {
                            return(LoopAction.Break);
                        }

                        Vector rate   = innerCache.Get();
                        Vector dfPay  = innerCache.Get();
                        Vector stdDev = innerCache.GetClear();
                        Vector amount = innerCache.GetClear();

                        GeneralCashflowProperties properties = fCashflows.GetCashflowProperties(i);

                        double tPay    = CalcUtils.DaysToYears(cashflow.Payment_Date - baseDate);
                        bool haveDfPay = false;
                        if (forecastIsDiscount && tPay == tDfStart)
                        {
                            dfPay.Assign(dfStart);
                            haveDfPay = true;
                        }

                        using (IntraValuationDiagnosticsHelper.StartCashflow(intraValuationDiagnosticsWriter))
                            using (var volatilitiesAtDateStore = IntraValuationDiagnosticsHelper.CreateVolatilitiesAtDateStore(intraValuationDiagnosticsWriter, pv.Count))
                            {
                                cashflow.AddPropertiesToIntraValuationDiagnostics(intraValuationDiagnosticsWriter);

                                // Standard Libor implies single reset.
                                var reset = cashflow.Resets.Single();

                                if (reset.IsKnown(baseDate))
                                {
                                    rate.Assign(reset.Known_Rate);
                                }
                                else
                                {
                                    double tValue = CalcUtils.DaysToYears(valueDate - baseDate);
                                    double tReset = CalcUtils.DaysToYears(reset.Reset_Date - baseDate);
                                    double tStart = CalcUtils.DaysToYears(reset.Rate_Start_Date - baseDate);
                                    double tEnd   = CalcUtils.DaysToYears(reset.Rate_End_Date - baseDate);

                                    // Reset is a historical or forward Libor rate.
                                    InterestRateUtils.LiborRate(rate, fForecastRate, tValue, tReset, tStart, tEnd, reset.Rate_Year_Fraction,
                                                                dfStart, ref tDfStart, dfEnd, ref tDfEnd);

                                    if (tReset > tValue)
                                    {
                                        GetStandardDeviation(stdDev, tValue, tReset, tStart, tEnd);
                                        volatilitiesAtDateStore.Add(valueDate, reset.Reset_Date, stdDev);
                                    }
                                }

                                if (!haveDfPay && forecastIsDiscount && tPay == tDfEnd)
                                {
                                    dfPay.Assign(dfEnd);
                                    haveDfPay = true;
                                }

                                // Add swaplet value
                                amount.AddProduct(properties.Swap_Multiplier, rate);

                                double tau = reset.Rate_Year_Fraction;
                                rate.Assign(1.0 + rate * tau);

                                // Add cap and floor option values.
                                AddOptionValue(amount, OptionType.Call, rate, properties.Cap_Strike, stdDev, tau, properties.Cap_Multiplier);
                                AddOptionValue(amount, OptionType.Put, rate, properties.Floor_Strike, stdDev, tau, properties.Floor_Multiplier);

                                amount.Assign(fBuySellSign * (cashflow.Fixed_Amount + cashflow.Notional * (amount + cashflow.Margin) * cashflow.Accrual_Year_Fraction));

                                IntraValuationDiagnosticsHelper.AddImpliedVolatilities(intraValuationDiagnosticsWriter, volatilitiesAtDateStore);
                                CFFixedList.RoundCashflow(amount, Cashflow_Rounding);
                                CFFixedList.UpdatePvAndCash(cashflow, baseDate, valueDate, haveDfPay ? null : fDiscountRate, null, amount,
                                                            dfPay, pv, cash, intraValuationDiagnosticsWriter);
                            }
                    }

                    return(LoopAction.Continue);
                });
            }
        }
        /// <summary>
        /// Prepare for valuation anything that is not dependent upon the scenario.
        /// </summary>
        public override void PreCloneInitialize(PriceFactorList factors, BaseTimeGrid baseTimes, RequiredResults requiredResults)
        {
            base.PreCloneInitialize(factors, baseTimes, requiredResults);

            CallableBondForward deal = (CallableBondForward)Deal;

            double           firstCallDate   = deal.First_Call_Date;
            double           lastCallDate    = deal.Last_Call_Date;
            double           baseDate        = factors.BaseDate;
            double           issueDate       = deal.Issue_Date;
            double           settlementDate  = deal.Settlement_Date;
            double           priceDate       = Math.Max(baseDate, settlementDate + 1.0); // bond cashflows before priceDate do not contribute to bond price
            double           maturityDate    = deal.Bond_Maturity_Date;
            double           couponInterval  = deal.Coupon_Interval;
            double           notional        = deal.Notional;
            IHolidayCalendar holidayCalendar = deal.GetHolidayCalendar();

            DateGenerationParams dateGenerationParams = new DateGenerationParams
            {
                EffectiveDate         = issueDate,
                MaturityDate          = maturityDate,
                AccrualDayCount       = deal.Accrual_Day_Count,
                FirstCouponDate       = deal.First_Coupon_Date,
                PenultimateCouponDate = deal.Penultimate_Coupon_Date,
                Amortisation          = deal.Amortisation,
                CouponPeriod          = couponInterval,
                Principal             = notional,
                PrincipalExchange     = PrincipalExchange.Start_Maturity,
                AccrualCalendar       = holidayCalendar
            };

            CashflowListDetail detail = CashflowGeneration.GenerateCashflowListDetail(dateGenerationParams);

            // Collect reset dates as we loop.
            var resetDates = new DateList(detail.Coupon_Details.Count);

            // Create cashflow list
            fCashflowList             = new CFFixedInterestList();
            fCashflowList.Compounding = YesNo.No;

            foreach (CouponDetail couponDetail in detail.Coupon_Details)
            {
                if (couponDetail.Payment_Date < priceDate)
                {
                    continue;
                }

                foreach (AccrualDetail accrualDetail in couponDetail.Accrual_Details)
                {
                    resetDates.Add(accrualDetail.Accrual_Start_Date);

                    if (couponDetail.Payment_Date < priceDate)
                    {
                        continue;
                    }

                    var cashflow = new CFFixedInterest
                    {
                        Payment_Date          = couponDetail.Payment_Date,
                        Notional              = accrualDetail.Notional,
                        Accrual_Start_Date    = accrualDetail.Accrual_Start_Date,
                        Accrual_End_Date      = accrualDetail.Accrual_End_Date,
                        Accrual_Year_Fraction = accrualDetail.Accrual_Year_Fraction,
                        Rate = deal.Coupon_Rate * Percentage.PercentagePoint,
                        Accrual_Day_Count = deal.Accrual_Day_Count,
                        Discounted        = YesNo.No
                    };

                    fCashflowList.Items.Add(cashflow);
                }
            }

            IRBaseDealSkin.ApplyRateSchedule(fCashflowList.Items, deal.Coupon_Rate_Schedule, Percentage.PercentagePoint, holidayCalendar, DateAdjustmentMethod.Modified_Following);

            // Calculate fixed interest cashflows.
            fCashflowList.CalculateInterest(baseDate);

            fFixedCashflowList = PrincipalCashflows(priceDate, issueDate, maturityDate, PrincipalExchange.Start_Maturity, notional, deal.Amortisation, 1.0);

            fSettlementAmount = 0.0;
            fAccrued          = 0.0;
            bool payDatesRequired = requiredResults.CashRequired();

            if (settlementDate >= baseDate)
            {
                double settlementPrincipal = CFFixedInterestListValuation.GetPrincipal(fCashflowList, settlementDate);
                fSettlementAmount = settlementPrincipal * deal.Price * Percentage.PercentagePoint;

                for (int i = 0; i < fCashflowList.Items.Count; ++i)
                {
                    CFFixedInterest cashflow = fCashflowList[i];

                    if (cashflow.Accrual_Start_Date >= settlementDate)
                    {
                        break;
                    }

                    if (settlementDate < cashflow.Accrual_End_Date)
                    {
                        fAccrued += cashflow.Interest() * (settlementDate - cashflow.Accrual_Start_Date) / (cashflow.Accrual_End_Date - cashflow.Accrual_Start_Date);
                    }
                }

                if (deal.Price_Is_Clean == YesNo.Yes)
                {
                    fSettlementAmount += fAccrued; // add accrued interest
                }
                fT.AddPayDate(settlementDate, payDatesRequired);
            }

            // Add the floating and fixed cashflow dates to the time grid.
            fT.AddPayDates <CFFixedInterest>(fCashflowList, payDatesRequired);
            fT.AddPayDates <CFFixed>(fFixedCashflowList, payDatesRequired);

            // We only need an option pricer if callable on or after the settlement date.
            fSwaptionPricer = null;
            if (lastCallDate >= settlementDate)
            {
                // Snap call dates to grid of reset dates and
                // ensure that first call date is on or after settlement date
                int iLast = resetDates.IndexOfNextDate(lastCallDate);
                lastCallDate = resetDates[iLast];
                int iFirst = resetDates.IndexOfNextDate(firstCallDate);

                while ((iFirst < resetDates.Count - 1) && (resetDates[iFirst] < settlementDate))
                {
                    // move first exercise date forward
                    iFirst++;
                }

                firstCallDate = resetDates[iFirst];
                int      paySign      = deal.Call_Put == OptionType.Put ? +1 : -1;
                RateList exerciseFees = new RateList();

                foreach (Rate price in deal.Call_Prices)
                {
                    Rate fee = new Rate();
                    fee.Value = paySign * (Percentage.OverPercentagePoint - price.Value);
                    fee.Date  = price.Date;
                    exerciseFees.Add(fee);
                }

                var amortisation = AllocateAmortisationToPaymentDates <CFFixedInterest>(deal.Amortisation, fCashflowList.Items);

                fSwaptionPricer = new SwaptionPricer(issueDate, maturityDate, couponInterval, couponInterval,
                                                     deal.Accrual_Day_Count, holidayCalendar, DayCount.ACT_365, holidayCalendar, firstCallDate, lastCallDate, baseDate,
                                                     paySign, paySign, 0.0, null, notional, amortisation, deal.Coupon_Rate, null, deal.Coupon_Rate_Schedule, exerciseFees,
                                                     null, OptionStyle2.Bermudan, Max_Nodes, Step_Size, fT, true, requiredResults.CashRequired());
            }

            if (NeedSurvivalProb())
            {
                fRecoveryList = new CFRecoveryList();
                fRecoveryList.PopulateRecoveryCashflowList(baseDate, settlementDate, fCashflowList);
            }
        }