/// <summary> /// Calculate valuation profiles. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { base.Value(valuationResults, factors, baseTimes); var accruedResults = valuationResults.Results <AccruedInterest>(); if (accruedResults == null) { return; } using (var cache = Vector.Cache(factors.NumScenarios)) { Vector accruedInterest = cache.Get(); var deal = (CFEquityFloatingInterestListDeal)Deal; var tgi = new TimeGridIterator(fT); var equityParams = new EquityCashflowParams((EquityPrice)fEquity, (EquityPriceVol)fEquityVol, fEquityFXRate, fEquityPayoffFXRate, deal.GetEquityPayoffType(), fEquityQuantoCompo, null, null); while (tgi.Next()) { deal.Cashflows.CalculateAccrual(accruedInterest, equityParams, factors.BaseDate, tgi.Date, accruedResults.AccrueFromToday, deal.GetHolidayCalendar(), deal.Accrual_Day_Count, fForecastRate, fFxRate); accruedResults.SetValue(tgi.Date, fBuySellSign * accruedInterest); } } }
/// <summary> /// Calculate valuation profiles. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { base.Value(valuationResults, factors, baseTimes); var deal = (CFFixedInterestListDeal)Deal; CalculateMetrics(valuationResults, factors, deal); var accruedResults = valuationResults.Results <AccruedInterest>(); if (accruedResults == null) { return; } var tgi = new TimeGridIterator(fT); while (tgi.Next()) { double accruedInterest = deal.Cashflows.CalculateAccrual(tgi.Date, accruedResults.AccrueFromToday, fDeal.GetHolidayCalendar()); accruedResults.SetValue(tgi.Date, fBuySellSign * accruedInterest); } }
/// <summary> /// Generic Deal Valuation - derived objects override the ValueFn() method. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); var deal = (CalendarSpreadOption)Deal; var tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; CashAccumulators accumulator = valuationResults.Cash; fTimeToSettlement = CalcUtils.DaysToYears(deal.Settlement_Date - factors.BaseDate); fPeriodEnd1 = CalcUtils.DaysToYears(deal.Period_End_1 - factors.BaseDate); fPeriodEnd2 = CalcUtils.DaysToYears(deal.Period_End_2 - factors.BaseDate); // The time to expiry is the time to the end of the first expiring contract. fTimeToExpiry = CalcUtils.DaysToYears(deal.Period_End_1 - factors.BaseDate); using (var cache = Vector.Cache(factors.NumScenarios)) { EnergySpreadOptionPricer pricer1 = GetEnergyOptionPricer(fForwardSample, fReferencePrice1, fReferenceVol1, factors.BaseDate, fPeriodEnd1, deal.Period_Start_1, deal.Period_End_1, deal.Realized_Average_1, deal.Realized_Average_Date_1, cache); EnergySpreadOptionPricer pricer2 = GetEnergyOptionPricer(fForwardSample, fReferencePrice2, fReferenceVol2, factors.BaseDate, fPeriodEnd2, deal.Period_Start_2, deal.Period_End_2, deal.Realized_Average_2, deal.Realized_Average_Date_2, cache); Vector pv = cache.Get(); Vector realizedPrice1 = cache.Get(); Vector realizedPrice2 = cache.Get(); Vector unrealizedPrice1 = cache.Get(); Vector unrealizedPrice2 = cache.Get(); Vector vol1 = cache.Get(); Vector vol2 = cache.Get(); Vector correlation = cache.Get(); Vector discountFactor = cache.Get(); // Calculate correlation between forward prices for two different (dereferenced) maturities (e.g. Apr2012 and Jan2013). TDate forwardDate1 = fReferencePrice1.GetPriceDate(deal.Period_End_1, 0); TDate forwardDate2 = fReferencePrice2.GetPriceDate(deal.Period_End_2, 0); fCorrelations.GetValue(correlation, tgi.T, forwardDate1, forwardDate2); while (tgi.Next()) { pv.Clear(); if (tgi.T <= fTimeToExpiry) { // Get unrealised reference forward prices (which also age the pricer to time t and update the realized average at time t) pricer1.GetUnrealizedPrice(tgi.T, unrealizedPrice1); pricer2.GetUnrealizedPrice(tgi.T, unrealizedPrice2); // Get the realized averages for both reference prices. pricer1.GetRealizedPrice(realizedPrice1); pricer2.GetRealizedPrice(realizedPrice2); int numSamples1 = pricer1.GetNumSamples(); int numRealizedSamples1 = pricer1.GetNumRealizedSamples(); int numUnrealizedSamples1 = pricer1.GetNumUnrealizedSamples(); int numSamples2 = pricer2.GetNumSamples(); int numRealizedSamples2 = pricer2.GetNumRealizedSamples(); int numUnrealizedSamples2 = pricer2.GetNumUnrealizedSamples(); // Modify the strike Vector kStar = cache.Get(deal.Strike_Price - realizedPrice1 * numRealizedSamples1 / numSamples1 + realizedPrice2 * numRealizedSamples2 / numSamples2); Vector refPriceStar1 = cache.Get(unrealizedPrice1 * numUnrealizedSamples1 / numSamples1); Vector refPriceStar2 = cache.Get(unrealizedPrice2 * numUnrealizedSamples2 / numSamples2); // Get ATM volatilities of the forward price at different maturities (given as time in years with respect to base date). pricer1.GetVol(tgi.T, unrealizedPrice2, vol1); pricer2.GetVol(tgi.T, unrealizedPrice1, vol2); // value is intrinsic - pricing with volatility 0 and realized price if there are no future sample. if (numUnrealizedSamples1 == 0) { vol1.Clear(); } // value is intrinsic - pricing with volatility 0 and realized price if there are no future sample. if (numUnrealizedSamples2 == 0) { vol2.Clear(); } // CSO pricing: exp(-rT) * E{max(F1(T) - F2(T) - K, 0)} // For the European CSO, we set multiplier1 = sign, multiplier2 = -sign, constant = -sign * strike, // where sign = +1 for a call and -1 for a put in the SpreadOptionBS function. double rootTime = Math.Sqrt(fTimeToExpiry - tgi.T); Vector volStar1 = cache.Get(vol1 * rootTime); Vector volStar2 = cache.Get(vol2 * rootTime); PricingFunctions.SpreadOptionBS(pv, fCallPutSign, -fCallPutSign, cache.Get(-fCallPutSign * kStar), fCallPutSign, refPriceStar1, refPriceStar2, kStar, volStar1, volStar2, correlation); // The option itself cannot be worth less than zero (for long positions). // However, due to that the Bjerksund & Stensland is a type of non-optimal exercise, it is possible to end up with negative PV via // its pricing formula. pv.AssignMax(pv, 0.0); // Discount payment made at settlement date pv.MultiplyBy(fScale * fDiscountRate.Get(tgi.T, fTimeToSettlement)); } // Cash settlement at settlement date if (tgi.T == fTimeToSettlement && accumulator != null && !accumulator.Ignore) { accumulator.Accumulate(fFxPayoffRate, tgi.Date, pv); } result.AppendVector(tgi.Date, pv * fFxPayoffRate.Get(tgi.T)); } result.Complete(fT); } }
/// <summary> /// Vector valuation function. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); TimeGridIterator tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; CashAccumulators accumulator = valuationResults.Cash; DealCreditLinkedNoteBase deal = (DealCreditLinkedNoteBase)fDeal; ISurvivalProb SP = GetSurvivalProbability(factors); RecoveryRate RR = GetRecoveryRate(factors); CreditRating CR = GetCreditRating(factors); double tEffective = CalcUtils.DaysToYears(deal.Effective_Date - factors.BaseDate); double scale = (deal.Buy_Sell == BuySell.Buy) ? +deal.Notional_Amount : -deal.Notional_Amount; double purchasePrice = Percentage.PercentagePoint * deal.Price; double couponRate = (deal.Coupon_Type == InterestRateType.Fixed) ? Percentage.PercentagePoint * deal.Coupon_Rate : 0.0; double couponSpread = (deal.Coupon_Type == InterestRateType.Fixed) ? 0.0 : BasisPoint.BasisPointValue * deal.Coupon_Spread; double indexTenor = (deal.Index_Tenor > 0.0) ? deal.Index_Tenor : deal.Coupon_Interval; using (var cache = Vector.Cache(factors.NumScenarios)) { bool[] hasDefaulted = (Respect_Default == YesNo.Yes) ? new bool[factors.NumScenarios] : null; Vector defaultTime = (Respect_Default == YesNo.Yes) ? cache.Get() : null; Vector historicalRecovery = (Respect_Default == YesNo.Yes) ? cache.GetClear() : null; if (hasDefaulted != null && CR != null) { DefaultTime(defaultTime, CR); } Vector npv = cache.Get(); Vector cash = cache.Get(); Vector pStart = cache.Get(); Vector pEnd = cache.Get(); Vector amount = cache.Get(); Vector recovery = cache.Get(); Vector dfLast = cache.Get(); Vector df = cache.Get(); cash.Clear(); var defaultedBeforeTheBaseDate = Respect_Default == YesNo.Yes && CreditRating.DefaultedBeforeBaseDate(CR, factors.BaseDate); while (tgi.Next()) { if (defaultedBeforeTheBaseDate) { npv.Clear(); result.AppendVector(tgi.Date, npv); break; } if (!deal.Principal_Guaranteed && Respect_Default == YesNo.Yes) { RealizedRecoveryRate(recovery, RR, tgi.T); } // Assume defaults are rare and start by valuing under all scenarios without realized defaults // Value of principal repayment SurvivalProbability(pEnd, factors, SP, tgi.T, fT.fLast); fDiscountRate.GetValue(dfLast, tgi.T, fT.fLast); if (deal.Principal_Guaranteed) { npv.Assign(dfLast); } else { npv.Assign(dfLast * pEnd); } if (accumulator != null && tgi.T == fT.fLast) { cash.Assign(npv); } // Value of coupons for (int i = PayDates.Count - 1; i >= 0 && tgi.Date <= PayDates[i]; --i) { double tPay = CalcUtils.DaysToYears(PayDates[i] - factors.BaseDate); double tReset = CalcUtils.DaysToYears(ResetDates[i] - factors.BaseDate); double tPrevious = Math.Max(tgi.T, tReset); SurvivalProbability(pStart, factors, SP, tgi.T, tPrevious); if (deal.Coupon_Type == InterestRateType.Floating) { // Forecast a coupon, add the spread InterestRateUtils.LiborRate(amount, fForecastRate, tgi.T, tReset, tReset, tReset + indexTenor, deal.Index_Day_Count); amount.Add(couponSpread); amount.MultiplyBy(Accruals[i]); } else { // Fixed coupon amount.Assign(couponRate * Accruals[i]); } // The value of the coupon if no default npv.Add(amount * fDiscountRate.Get(tgi.T, tPay) * pEnd); if (accumulator != null && tgi.T == tPay) { cash.Assign(amount); } // The recovery value on default - assume guaranteed principal paid at end, recovery paid immediately if (!deal.Principal_Guaranteed) { npv.Add(fDiscountRate.Get(tgi.T, 0.5 * (tPay + tPrevious)) * (pStart - pEnd) * PricingRecoveryRate(SP)); } pEnd.DestructiveAssign(pStart); } // Now check for realized default scenario by scenario, overwriting NPV and cash as appropriate if (Respect_Default == YesNo.Yes && defaultTime != null) { if (tgi.T < tEffective) { fDiscountRate.GetValue(df, tgi.T, tEffective); } for (int i = 0; i < npv.Count; ++i) { if (defaultTime[i] > tgi.T) { continue; } if (deal.Principal_Guaranteed) { npv[i] = dfLast[i]; // full principal paid at maturity } else { if (!hasDefaulted[i]) { historicalRecovery[i] = recovery[i]; // record the historical recovery rate } if (tgi.T < tEffective) { npv[i] = df[i] * historicalRecovery[i]; // The discounted recovery value of the principal will be paid out on the effective date } else if (tgi.T == tEffective || !hasDefaulted[i]) { npv[i] = historicalRecovery[i]; // The full recovery amount is paid out } else { npv[i] = 0.0; // default is in the past but we are after effective date; settlement has already occurred. } } hasDefaulted[i] = true; } } // Value of purchase price if (tgi.T < tEffective) { npv.Add(-purchasePrice * fDiscountRate.Get(tgi.T, tEffective)); } else if (tgi.T == tEffective) { npv.Add(-purchasePrice); if (accumulator != null) { cash.Add(-purchasePrice); } } result.AppendVector(tgi.Date, scale * npv * fFxRate.Get(tgi.T)); if (accumulator != null) { accumulator.Accumulate(fFxRate, tgi.Date, scale * cash); } } // After maturity result.Complete(fT); } }
/// <summary> /// Aggregate the valuation profile onto a set of result curves to support result partitioning. /// </summary> protected override void ProcessResults(ValuationResults valResults, DealPartitionAssociations assoc, PriceFactorList factors, BaseTimeGrid baseTimes, ValuationOptions options, int partition) { var pvProfiles = valResults.Results <PVProfiles>(); var addOnProfiles = valResults.Results <AddOnProfiles>(); var positiveMtmProfiles = valResults.Results <PositiveMtmProfiles>(); Debug.Assert(addOnProfiles != null, "No Add-On profiles. Cannot proceed with valuation."); Debug.Assert(positiveMtmProfiles != null, "No Positive mtM profiles. Cannot proceed with valuation."); fT = Deal.ValuationGrid(factors, baseTimes, Deal.EndDate()); var tgi = new TimeGridIterator(fT); var nettedExposure = new PVProfiles(factors.NumScenarios); var collateralExposure = new PVProfiles(factors.NumScenarios); var addOnsProfile = new PVProfiles(factors.NumScenarios); var mtmTermProfile = new PVProfiles(factors.NumScenarios); DealBaselNettingCollateralSet nettingSetDeal = Deal as DealBaselNettingCollateralSet; bool collateralised = nettingSetDeal.Collateralised == YesNo.Yes; using (var cache = Vector.Cache(factors.NumScenarios)) { Vector sumMtm = cache.Get(); Vector sumPositiveMtm = cache.Get(); Vector addOns = cache.Get(); Vector netGrossRatio = cache.Get(); Vector value = cache.Get(); Vector term1 = cache.Get(); Vector term2 = cache.Get(); // Collateral related vectors. Vector mtmTermStart = cache.Get(); Vector addOnHp = cache.Get(); // Loop to get the netting set exposure. while (tgi.Next()) { sumMtm.Clear(); sumPositiveMtm.Clear(); addOns.Clear(); value.Clear(); double date = tgi.Date; // For MtM Plus Add-On deals PV profiles represents the sum of the MtM profile and Add-On profile. // Subtract the Add-On profile to recover the MtM profile before flooring. sumMtm.Assign(VectorMath.Max(pvProfiles[date] - addOnProfiles[date], 0.0)); addOns.Assign(addOnProfiles[date]); sumPositiveMtm.Assign(positiveMtmProfiles[date]); netGrossRatio.AssignConditional(sumPositiveMtm > 0, sumMtm / sumPositiveMtm, 0.0); netGrossRatio.MultiplyBy(this.fNetGrossRatioCorrelation); netGrossRatio.Add(1 - this.fNetGrossRatioCorrelation); term2.AssignProduct(addOns, netGrossRatio); term1.Assign(VectorMath.Max(sumMtm, 0.0)); value.AssignSum(term1, term2); nettedExposure.AppendVector(date, value); if (collateralised) { mtmTermProfile.AppendVector(date, term1); addOnsProfile.AppendVector(date, term2); } } nettedExposure.Complete(this.fT); var exposureResults = valResults.Results <Exposure>(); if (exposureResults != null) { exposureResults.Assign(nettedExposure); } // Collateral cases. if (collateralised) { mtmTermProfile.Complete(this.fT); addOnsProfile.Complete(this.fT); double date = factors.BaseDate; mtmTermProfile.GetValue(mtmTermStart, date); addOnsProfile.GetValue(addOnHp, date + nettingSetDeal.Holding_Period); // Assume we have post haircut collateral. double collateral = nettingSetDeal.Balance; double threshold = nettingSetDeal.Threshold; tgi.Reset(); // Loop to get the netting set exposure. while (tgi.Next()) { bool inHoldingPeriod = tgi.T < CalcUtils.DaysToYears(nettingSetDeal.Holding_Period); CollateralBasel3(mtmTermStart, collateral, addOnHp, threshold, inHoldingPeriod, tgi.Date, nettedExposure, collateralExposure); } collateralExposure.Complete(this.fT); if (exposureResults != null) { exposureResults.Assign(collateralExposure); } } } if (options.PartitionCollateralMode != PartitionCollateralMode.Suppress_Collateral_And_Flooring || partition < options.NumTotalPartitions) { valResults.FloorResult(assoc.AggregationMode, options); } CollateralPlugIn.CollateralBalancesContainer coProfiles = valResults.Results <CollateralPlugIn.CollateralBalancesContainer>(); // Store collateral information according to diagnostic collection rules. if (coProfiles != null) { coProfiles.StoreInformation(this); } }
/// <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); }