/// <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> /// Fills the arrays of coupon, pay date discount factors, standard deviations and coefficient by running through the cashflows /// </summary> private void GetSwapQuantities(Vector[] coupon, Vector[] stdDev, Vector[] coefficient, Vector[] df, double tValue, double tExpiry, double baseDate, Vector dfTExpiry) { int count = coupon.Length; double tLastEnd = double.NegativeInfinity; double tLastPay = double.NegativeInfinity; int floatIndex = 0; CFFloatingInterest cfFloating = fFloatCashflowList[floatIndex]; TDate floatingStartDate = cfFloating.Resets[0].Rate_Start_Date; // We will minimise the number of discount factors we get. bool[] haveDF = new bool[count]; using (var cache = Vector.CacheLike(dfTExpiry)) { Vector bTExpiry = cache.Get(); Vector rootZeta = cache.Get(); Vector bT = cache.Get(); Vector dfTStart = cache.Get(); Vector dfTPay = cache.Get(); Vector ffTStart = cache.Get(); Vector ffTEnd = cache.Get(); Vector beta = cache.Get(); fModelParameters.GetB(bTExpiry, tValue, tExpiry); fModelParameters.GetZeta(rootZeta, tValue, tExpiry); rootZeta.AssignSqrt(rootZeta); // Loop over copuons calculating useful quantities. VectorEngine.For(0, count, i => { double tDf = CalcUtils.DaysToYears(fDates[i] - baseDate); // if this date is associated with a floating start date include beta contribution if (fDates[i] == floatingStartDate) { var floatingEndDate = cfFloating.Resets[0].Rate_End_Date; var paymentDate = cfFloating.Payment_Date; double tStart = CalcUtils.DaysToYears(floatingStartDate - baseDate); double tEnd = CalcUtils.DaysToYears(floatingEndDate - baseDate); double tPay = CalcUtils.DaysToYears(paymentDate - baseDate); GetDiscountAndForecastFactors(dfTStart, dfTPay, ffTStart, ffTEnd, tValue, tStart, tEnd, tPay, tLastEnd, tLastPay); beta.Assign(dfTPay * ffTStart / (dfTStart * ffTEnd)); double rateYearFraction = cfFloating.Resets[0].Rate_Year_Fraction; double yearFractionRatio = (rateYearFraction < CalcUtils.TINY) ? 1.0 : cfFloating.Accrual_Year_Fraction / rateYearFraction; coupon[i].Assign(-beta * yearFractionRatio * cfFloating.Notional); // Store new discount factors. int payIndex = fDates.IndexOf(paymentDate); df[payIndex].Assign(dfTPay); haveDF[payIndex] = true; if (!haveDF[i]) { df[i].Assign(dfTStart); haveDF[i] = true; } // Update for next pass tLastEnd = tEnd; tLastPay = tPay; floatIndex++; if (floatIndex < fFloatCashflowList.Count()) { cfFloating = fFloatCashflowList[floatIndex]; floatingStartDate = cfFloating.Resets[0].Rate_Start_Date; } else { floatingStartDate = double.PositiveInfinity; } } // Work out coupon, stdDev, coefficient and dfTPay coupon[i].Add(fFloatingCouponWeight[i]); if (fSwapRate != null) { coupon[i].Add(fSwapRate * fFixedCouponWeight[i]); } else { coupon[i].Add(fFixedCouponRate[i] * fFixedCouponWeight[i]); } fModelParameters.GetB(bT, tValue, tDf); stdDev[i].Assign((bT - bTExpiry) * rootZeta); // Make sure we get all the discount factors. if (!haveDF[i]) { fDiscountRate.GetValue(df[i], tValue, tDf); haveDF[i] = true; } // Performs coefficient[i] = dfTPay[i] / dfTExpiry * Exp(-0.5 * stdDev[i] * stdDev[i]) coefficient[i].Assign(CalcUtils.SafeExpMultiply(-0.5 * stdDev[i] * stdDev[i], df[i] / dfTExpiry)); return(LoopAction.Continue); }); } }