/// <summary> /// Adds the cap or floor option value. /// </summary> private static void AddOptionValue(Vector amount, OptionType optionType, Vector rate, double strike, Vector stdDev, double tau, double multiplier) { if (multiplier != 0.0 && tau > 0.0) { double optionStrike = 1.0 + tau * strike; amount.AddProduct(multiplier / tau, PricingFunctions.BlackFunction(optionType, rate, optionStrike, stdDev)); } }
/// <summary> /// Value the deal on all dates within the valuation grid (vectorised). /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { var deal = (AverageForwardExplicitDealBase)Deal; double tMaturity = CalcUtils.DaysToYears(deal.Maturity_Date - factors.BaseDate); // Discount Factor and Forward Factor times are in Act365. int numSamplingDates = fSamplingTimes.Length; PreValue(factors); PVProfiles result = valuationResults.Profile; CashAccumulators cash = valuationResults.Cash; var tgi = new TimeGridIterator(fT); using (var pricerCache = Vector.Cache(factors.NumScenarios)) { double historicalObservations; double futureObservations; int currentDateCount; Vector realisedSum = pricerCache.GetClear(); // sum to value date. Vector forecastSum = pricerCache.Get(); Vector spotPrice = pricerCache.Get(); Vector forwardPrice = pricerCache.Get(); double tSpotPrice = double.NegativeInfinity; GetForwardPrice(0.0, deal.Tenor, true, ref tSpotPrice, spotPrice, forwardPrice); fSamplingData.GetInitialSum(null, factors.BaseDate, forwardPrice, realisedSum, out historicalObservations, out futureObservations, out currentDateCount); VectorEngine.For(tgi, () => { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector overallAverage = cache.Get(); Vector value = cache.Get(); Vector payoffRate = cache.Get(); UpdateSum(tgi.Date, realisedSum, ref tSpotPrice, spotPrice, ref historicalObservations, ref futureObservations, ref currentDateCount); forecastSum.Clear(); // all the sampling dates that are in the future (compared to our valuation date) VectorEngine.For(currentDateCount, numSamplingDates, i => { GetForwardPrice(tgi.T, fSamplingTimesPlusTenor[i], false, ref tSpotPrice, spotPrice, forwardPrice); forecastSum.AddProduct(fSamplingData[i].Weight, forwardPrice); return(LoopAction.Continue); }); forecastSum.MultiplyBy(spotPrice); double totalWeight = historicalObservations + futureObservations; if (totalWeight > 0.0) { overallAverage.Assign((realisedSum + forecastSum) / totalWeight); } else { overallAverage.Clear(); } PayoffRate(payoffRate, overallAverage, ref tSpotPrice, spotPrice, tgi.T, tMaturity); value.Assign(fScale * fDiscountRate.Get(tgi.T, tMaturity) * payoffRate); if (tgi.Date == deal.Maturity_Date) { cash.Accumulate(fPayoffFxRate, tgi.Date, value); } result.AppendVector(tgi.Date, value * fPayoffFxRate.Get(tgi.T)); } }); result.Complete(fT); } }
/// <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> /// Calculate valuation profiles. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); TimeGridIterator tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; CashAccumulators cashAccumulator = valuationResults.Cash; double baseDate = factors.BaseDate; CallableStructuredDeal deal = (CallableStructuredDeal)fDeal; int buySellSign = deal.Buy_Sell == BuySell.Buy ? +1 : -1; int callPutSign = deal.Option_Type == OptionType.Call ? 1 : -1; InterestRateOptionPricer optionPricer = CreateOptionPricer(factors); CalcUtils.CreateDealProfilesIfRequired(valuationResults, fItems, factors); bool needRating = Respect_Default == YesNo.Yes && !string.IsNullOrEmpty(deal.Issuer); using (var cache = Vector.Cache(factors.NumScenarios)) { Vector exercised = cache.GetClear(); // vector taking value 0 or 1 indicating exercise before tgi.date Vector exercisedToday = cache.Get(); // vector taking value 0 or 1 indicating exercise at tgi.date Vector optionPv = cache.Get(); Vector pv = cache.Get(); Vector cash = cache.Get(); Vector settlementDateAtExercise = cache.GetClear(); Vector defaultDate = needRating ? cache.Get(CalcUtils.DateTimeMaxValueAsDouble) : null; var defaultedBeforeBaseDate = needRating && CreditRating.DefaultedBeforeBaseDate(fCreditRating, baseDate); while (tgi.Next()) { if (defaultedBeforeBaseDate) { pv.Clear(); result.AppendVector(tgi.Date, pv); break; } if (needRating) { UpdateDefaultDate(fCreditRating, tgi.Date, tgi.T, defaultDate); } double val; bool allExercised = exercised.AllElementsTheSame(out val) && val == 1.0; if (deal.Settlement_Style == SettlementType2.Physical) { // Calculate value of option (option value is zero after last exercise date) if (!allExercised) { optionPricer.Value(baseDate, tgi.Date, optionPv, exercised, exercisedToday, settlementDateAtExercise, defaultDate); } // Calculate value of underlying cashflows after settlementDateAtExercise pv.Clear(); cash.Clear(); InterestRateOptionPricer.ValueDeals(fItems, pv, cash, baseDate, tgi.Date, settlementDateAtExercise, defaultDate, fDiscountRate, fForecastRate, fRepoRate, fInterestRateVol, fInterestYieldVol, fSurvivalProb, fRecoveryRate); pv.MultiplyBy(callPutSign); cash.MultiplyBy(callPutSign); if (!allExercised) { // If exercised today the cashflow is the value of the option minus the value of the physically settled part // Else if already exercised, cash is the unnderlying cash. // Else (before exercise) there is no cash. cash.AssignConditional(exercisedToday, optionPv - pv, exercised * cash); // If already exercised, pv is the unnderlying pv. // Else (before exercise or exercised today), pv is the option pv. pv.AssignConditional(exercised, pv, optionPv); pv.AssignConditional(exercisedToday, optionPv, pv); } } else { if (allExercised) { // Already exercised on all scenarios result.AppendZeroVector(tgi.Date); continue; } if (deal.Settlement_Style == SettlementType2.Cash) { // Calculate value of option optionPricer.Value(baseDate, tgi.Date, pv, exercised, exercisedToday, settlementDateAtExercise, defaultDate); // If exercised today then option pv is settled today, otherwise there is no cash cash.AssignProduct(pv, exercisedToday); } else // Embedded option (callable or puttable) { // Calculate underlying value pv.Clear(); cash.Clear(); InterestRateOptionPricer.ValueDeals(fItems, pv, cash, baseDate, tgi.Date, null, defaultDate, fDiscountRate, fForecastRate, fRepoRate, fInterestRateVol, fInterestYieldVol, fSurvivalProb, fRecoveryRate); // Calculate value of option optionPricer.Value(baseDate, tgi.Date, optionPv, exercised, exercisedToday, settlementDateAtExercise, defaultDate); // Add or subtract value of embedded option pv.AddProduct(-callPutSign, optionPv); // Option payoff is Max(callPutSign * (underlyingPv - accruedInterest - discountedFee), 0) // Callable/puttable payoff on exercise is // underlyingPv - callPutSign * (callPutSign * (underlyingPv - accruedInterest - discountedFee)) // = accruedInterest + discountedFee // Set pv and cash to zero if already exercised. // If exercised today then the pv is settled today. pv.AssignConditional(exercised, exercisedToday * pv, pv); cash.AssignConditional(exercised, exercisedToday * pv, cash); } } pv.MultiplyBy(buySellSign); cash.MultiplyBy(buySellSign); result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * pv); cashAccumulator.Accumulate(fFxRate, tgi.Date, cash); } } result.Complete(fT); }