/// <summary> /// Calculate valuation profiles. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { base.Value(valuationResults, factors, baseTimes); var deal = (CFFloatingInterestListDeal)Deal; CalculateMetrics(valuationResults, factors, deal); var accruedResults = valuationResults.Results <AccruedInterest>(); if (accruedResults == null) { return; } using (var cache = Vector.Cache(factors.NumScenarios)) { Vector accruedInterest = cache.Get(); var tgi = new TimeGridIterator(fT); VectorEngine.For(tgi, () => { fCashflows.CalculateAccrual(accruedInterest, factors.BaseDate, tgi.Date, accruedResults.AccrueFromToday, deal.GetHolidayCalendar(), fForecastRate); accruedResults.SetValue(tgi.Date, fBuySellSign * accruedInterest); }); } }
/// <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 cashAccumulators = valuationResults.Cash; BaseCliquetOption deal = (BaseCliquetOption)Deal; double scale = (deal.Buy_Sell == BuySell.Buy ? +1 : -1) * deal.GetUnits(); VectorEngine.For(tgi, () => { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector pv = cache.Get(); Vector cash = cache.Get(); PricingFunctions.CliquetOption(pv, cash, deal.Option_Type, tgi.T, fTimes, deal.Moneyness, fKnownPrices, fAssetPrice, fFxRate, fPayoffFxRate, fDiscountRate, fAssetPriceVol, fQuantoCompo, fPayoffType, factors.PathDependent); cashAccumulators.Accumulate(fPayoffFxRate, tgi.Date, scale * cash); result.AppendVector(tgi.Date, scale * pv * fPayoffFxRate.Get(tgi.T)); } } ); result.Complete(fT); }
/// <summary> /// Checks a sufficient condition for yStar to be the unique solution of F(y) = 0. /// </summary> private void IsSolutionUnique(Vector isYStarUnique, Vector yStar, Vector[] coefficient, Vector[] coupon, Vector[] stdDev) { using (var cache = Vector.CacheLike(yStar)) { Vector sum = cache.GetClear(); Vector haveNegative = cache.GetClear(); Vector positiveValue = cache.Get(); int count = coefficient.Length; // C_0 * f_0 (y*) - this term is negative. sum.Assign(CalcUtils.SafeExpMultiply(-stdDev[0] * yStar, coupon[0] * coefficient[0])); VectorEngine.For(1, count, LoopDirection.Backwards, i => { haveNegative.AssignConditional(coupon[i] <= -CalcUtils.TINY, 1.0, haveNegative); positiveValue.Assign(CalcUtils.SafeExpMultiply(-stdDev[i] * yStar, coupon[i] * coefficient[i] * haveNegative)); positiveValue.AssignMax(positiveValue, 0.0); sum.Add(positiveValue); return(LoopAction.Continue); }); isYStarUnique.AssignConditional(sum <= 0.0, 1.0, 0.0); } }
/// <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 cashAccumulators = valuationResults.Cash; FixedCashflowBaseDeal deal = (FixedCashflowBaseDeal)fDeal; double payDate = deal.Payment_Date; double tPay = CalcUtils.DaysToYears(payDate - factors.BaseDate); VectorEngine.For(tgi, () => { if (tgi.Date == payDate) { result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * fAmount); } else { result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * fDiscountRate.Get(tgi.T, tPay) * fAmount); } }); if (!cashAccumulators.Ignore && factors.BaseDate <= payDate && payDate <= fT.fHorizon) { cashAccumulators.Accumulate(fFxRate, payDate, fAmount); } result.Complete(fT); }
/// <summary> /// Calculate vector valuation profile and vector realised cash profile. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); CalcUtils.CreateDealProfilesIfRequired(valuationResults, fItems, factors); double paySign = fSwaptionDeal.Payer_Receiver == PayerReceiver.Payer ? +1 : -1; double buySign = fSwaptionDeal.Buy_Sell == BuySell.Buy ? +1 : -1; bool isCashSettled = fSwaptionDeal.Settlement_Style == SettlementType.Cash; bool isPhysicallySettled = fSwaptionDeal.Settlement_Style == SettlementType.Physical; bool cashRequired = !valuationResults.Cash.Ignore; TimeGridIterator tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; using (IntraValuationDiagnosticsHelper.StartDeal(fIntraValuationDiagnosticsWriter, Deal)) { using (var outerCache = Vector.Cache(factors.NumScenarios)) { Vector pv = outerCache.Get(); Vector exerciseWeight = outerCache.GetClear(); Vector cash = cashRequired ? outerCache.GetClear() : null; // For a cash settled swaption, Settlement amount to be paid on Settlement Date. Vector settlementCash = isCashSettled ? outerCache.GetClear() : null; VectorEngine.For(tgi, () => { // Work out the PV if (tgi.Date < fSwaptionDeal.Option_Expiry_Date) { ValueBeforeExpiry(pv, factors, isCashSettled, tgi); } else { ValueOnOrAfterExpiry(pv, exerciseWeight, settlementCash, cash, factors, isCashSettled, isPhysicallySettled, cashRequired, tgi, paySign); } result.AppendVector(tgi.Date, buySign * pv * fFxRate.Get(tgi.T)); if (cashRequired) { valuationResults.Cash.Accumulate(fFxRate, tgi.Date, buySign * cash); } }); } result.Complete(fT); } }
private static void CreateVectorArrays(Vector[] df, VectorScopedCache.Scope cache, Vector[] coupon, Vector[] coefficient, Vector[] stdDev) { int count = df.Length; VectorEngine.For(0, count, i => { df[i] = cache.GetClear(); coupon[i] = cache.GetClear(); coefficient[i] = cache.GetClear(); stdDev[i] = cache.GetClear(); return(LoopAction.Continue); }); }
/// <summary> /// Calculate valuation profiles. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { base.PreValue(factors); IAssetPrice commodityPrice; ISpotProcessVol dummyAssetPriceVol; ((BaseAssetFxDealHelper)GetDealHelper()).PreValueAsset(out commodityPrice, out dummyAssetPriceVol, out fBasketPricer, ref fQuantoCompo, factors); CommodityForwardDealBase deal = (CommodityForwardDealBase)fDeal; double scale = (deal.Buy_Sell == BuySell.Buy ? +1 : -1) * deal.Units; double tMaturity = CalcUtils.DaysToYears(deal.Maturity_Date - factors.BaseDate); TimeGridIterator tgi = new TimeGridIterator(fT); CashAccumulators cash = valuationResults.Cash; PVProfiles result = valuationResults.Profile; VectorEngine.For(tgi, () => { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector pv = cache.Get(); if (tgi.Date <= deal.Maturity_Date) { pv.Assign(commodityPrice.ForwardFactor(tgi.T, tMaturity, fFxRate) * commodityPrice.Get(tgi.T)); // assign forward * fxRate to pv pv.Assign((pv - deal.Forward_Price * fFxRate.Get(tgi.T)) * fDiscountRate.Get(tgi.T, tMaturity) * scale); } else { pv.Clear(); } result.AppendVector(tgi.Date, pv); if (tgi.Date == deal.Maturity_Date) { cash.Accumulate(fFxRate, deal.Maturity_Date, (commodityPrice.Get(tMaturity) / fFxRate.Get(tMaturity) - deal.Forward_Price) * scale); } } }); result.Complete(fT); }
/// <summary> /// Calculates the PV using Gauss Hermite quadrature if at least one scenario needs it. /// </summary> private void CalculateNumericalPV(Vector numericalPv, Vector isUnique, Vector[] stdDev, Vector[] coefficient, Vector[] coupon) { if (isUnique.MinElement() == 0.0) { double delta = fSwaptionDeal.Payer_Receiver == PayerReceiver.Payer ? 1.0 : -1.0; // At least one scenario needs numerical integration CalcUtils.GaussianQuadratureIntegral(numericalPv, (vout, y) => { vout.Clear(); VectorEngine.For(0, coupon.Length, i => { vout.Subtract(CalcUtils.SafeExpMultiply(stdDev[i] * y, delta * coupon[i] * coefficient[i])); return(LoopAction.Continue); }); vout.Assign(VectorMath.Max(vout, 0.0)); }, fQuadrature.Value); } }
/// <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; CommitmentDeal deal = (CommitmentDeal)Deal; VectorEngine.For(tgi, () => { if (tgi.Date < deal.Effective_Date) { result.AppendZeroVector(tgi.Date); } else { double amount = deal.Amortisation.GetPrincipal(deal.Amount, tgi.Date - 1.0); result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * amount); } }); result.Complete(fT); }
/// <summary> /// Calculate a valuation profile for a range of scenarios. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { IAssetPrice price = GetAssetPrice(factors); PVProfiles result = valuationResults.Profile; double scale = fDeal.Units * (fDeal.Buy_Sell == BuySell.Buy ? +1 : -1); var tgi = new TimeGridIterator(fT); VectorEngine.For(tgi, () => result.AppendVector(tgi.Date, scale * price.Get(tgi.T))); result.Complete(fT); CashAccumulators cashAccumulators = valuationResults.Cash; double endDate = Deal.EndDate(); if (!cashAccumulators.Ignore && endDate <= fT.fHorizon) { double tEnd = CalcUtils.DaysToYears(endDate - factors.BaseDate); IFxRate fxRate = factors.GetInterface <IFxRate>(fDeal.Currency); cashAccumulators.Accumulate(fxRate, endDate, scale * price.Get(tEnd) / fxRate.Get(tEnd)); } }
/// <summary> /// Calculates the PV using analytic formula if at least one scenario needs it. /// </summary> private void CalculateAnalyticPV(Vector analyticPv, Vector isUnique, Vector[] stdDev, Vector yStar, Vector[] coefficient, Vector[] coupon, Vector dfTExpiry, Vector[] df) { if (isUnique.MaxElement() == 1.0) { OptionType optionType = fSwaptionDeal.Payer_Receiver == PayerReceiver.Payer ? OptionType.Call : OptionType.Put; // Value by summing the of the caplets or floorlets analyticPv.Clear(); using (var cache = Vector.CacheLike(yStar)) { // f_i(y*) plays the role of price and dfTPay[i] the strike. Vector optionletPrice = cache.Get(); VectorEngine.For(0, stdDev.Length, i => { // Performs optionletPrice = dfExpiry * fCoefficient[i] * Exp(-stdDev[i] * yStar) optionletPrice.Assign(CalcUtils.SafeExpMultiply(-stdDev[i] * yStar, coefficient[i] * dfTExpiry)); analyticPv.Add(coupon[i] * PricingFunctions.BlackFunction(optionType, optionletPrice, df[i], stdDev[i])); return(LoopAction.Continue); }); } } }
/// <summary> /// Calculate a valuation profile for the deal for the current scenario. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); var deal = (CFListBaseDeal <TCashflowList>)fDeal; double baseDate = factors.BaseDate; var tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; CashAccumulators cashAccumulators = valuationResults.Cash; double endDate = deal.EndDate(); using (IntraValuationDiagnosticsHelper.StartDeal(fIntraValuationDiagnosticsWriter, fDeal)) { using (var outerCache = Vector.Cache(factors.NumScenarios)) { Vector defaultDate = fCreditRating != null?outerCache.Get(CalcUtils.DateTimeMaxValueAsDouble) : null; var defaultedBeforeBaseDate = CreditRating.DefaultedBeforeBaseDate(fCreditRating, baseDate); bool collectCash = ValueOnCashflowDates(); var saccrResult = SACCRResultFactory.Create(valuationResults, deal.GetDealReferenceProvider().DealReference, () => new SACCROptionResult(factors.NumScenarios)); VectorEngine.For(tgi, () => { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector pv = cache.GetClear(); Vector cash = collectCash ? cache.GetClear() : null; if (!defaultedBeforeBaseDate) { using (IntraValuationDiagnosticsHelper.StartCashflowsOnDate(fIntraValuationDiagnosticsWriter, tgi.Date)) { using (IntraValuationDiagnosticsHelper.StartCashflows(fIntraValuationDiagnosticsWriter, fFxRate, tgi.T, deal)) { Value(pv, cash, baseDate, tgi.Date, saccrResult, fIntraValuationDiagnosticsWriter); IntraValuationDiagnosticsHelper.AddCashflowsPV(fIntraValuationDiagnosticsWriter, pv); } } if (fCreditRating != null) { UpdateDefaultDate(fCreditRating, tgi.Date, tgi.T, defaultDate); GetDefaultValue(baseDate, tgi.Date, defaultDate, fRecoveryRate, pv, cash); } } result.AppendVector(tgi.Date, pv * fFxRate.Get(tgi.T)); if (!cashAccumulators.Ignore && cash != null) { // Realise all value as cash on deal end date if (tgi.Date == endDate) { cash.Assign(pv); } cashAccumulators.Accumulate(fFxRate, tgi.Date, cash); } } }); if (!cashAccumulators.Ignore && !collectCash) { CollectCashflows(cashAccumulators, baseDate, fT.fHorizon); // Consolidate and net in order to avoid getting Net incoming and outgoing cashflows with the same payment date, // e.g. for compounding swaps with both positive and negative rates. cashAccumulators.ConsolidateAndNet(fCurrency, factors); } 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> /// Valuation method. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { CDOValuationParameters parameters = GetValuationParameters(factors); double scale = (fDeal.Buy_Sell == BuySell.Buy) ? +fDeal.Principal : -fDeal.Principal; double trancheSize = fDeal.Detachment - fDeal.Attachment; if (trancheSize < CalcUtils.TINY) { return; } double tUpfront = CalcUtils.DaysToYears(fDeal.Upfront_Date - factors.BaseDate); TimeGridIterator tgi = new TimeGridIterator(fT); CashAccumulators accumulator = valuationResults.Cash; PVProfiles result = valuationResults.Profile; using (IntraValuationDiagnosticsHelper.StartDeal(fIntraValuationDiagnosticsWriter, Deal)) { VectorEngine.For(tgi, () => { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector npv = cache.Get(); Vector expectedWritedownPremiumNotional = cache.Get(); Vector expectedLoss = cache.Get(); Vector expectedRecovery = cache.Get(); Vector discountFactor = cache.Get(); Vector realizedIndexLoss = cache.Get(); Vector realizedIndexRecovery = cache.Get(); Vector adjustedAttachment = cache.Get(); Vector adjustedDetachment = cache.Get(); Vector trancheRemainder = cache.Get(); // Handle upfront payment if (fDeal.Upfront_Date >= tgi.Date) { npv.Assign(scale * parameters.DF.Get(tgi.T, tUpfront) * fDeal.Upfront_Amount); } else { npv.Clear(); } // reinitialise running variables expectedWritedownPremiumNotional.Clear(); expectedLoss.Clear(); expectedRecovery.Clear(); discountFactor.Assign(parameters.DF.Get(tgi.T, tgi.T)); if (accumulator != null && tgi.Date == fDeal.Upfront_Date) { accumulator.Accumulate(parameters.X, tgi.Date, fDeal.Upfront_Amount); } // Check for realized loss and recovery and adjust the attachment and detachment accordingly parameters.RealizedLoss(realizedIndexLoss, realizedIndexRecovery, tgi.T, fDeal.Payoff_Is_Digital == YesNo.Yes, fDeal.Digital_Payoff_Percentage); adjustedDetachment.Assign(VectorMath.Max(0.0, VectorMath.Min(1.0 - realizedIndexRecovery, fDeal.Detachment) - realizedIndexLoss)); adjustedAttachment.Assign(VectorMath.Max(0.0, VectorMath.Min(1.0 - realizedIndexRecovery, fDeal.Attachment) - realizedIndexLoss)); trancheRemainder.Assign((adjustedDetachment - adjustedAttachment) / trancheSize); if (adjustedDetachment.MaxElement() > CalcUtils.TINY) { // Diagnostics double sumDefaultAccrual = 0; double sumPVPremium = 0; double sumPVProtection = 0; bool needDiagnostics = tgi.T == 0.0 && fIntraValuationDiagnosticsWriter.Level > IntraValuationDiagnosticsLevel.None; using (needDiagnostics ? IntraValuationDiagnosticsHelper.StartCDO(fIntraValuationDiagnosticsWriter, tgi.Date, fDeal.Principal) : null) { // Value future coupon periods VectorEngine.For(0, PayDates.Count, i => { if (PayDates[i] < tgi.Date) { return(LoopAction.Continue); } double tPay = CalcUtils.DaysToYears(PayDates[i] - factors.BaseDate); using (var innerCache = Vector.CacheLike(npv)) { Vector oldExpectedLoss = innerCache.Get(expectedLoss); Vector oldDiscountFactor = innerCache.Get(discountFactor); Vector oldExpectedWritedownPremiumNotional = innerCache.Get(expectedWritedownPremiumNotional); Vector expectedLossAttachment = innerCache.Get(); Vector expectedLossDetachment = innerCache.Get(); Vector premiumLeg = innerCache.Get(); Vector defaultLeg = innerCache.Get(); Vector accruedInDefault = innerCache.Get(); Vector expectedRecoveryAttachment = innerCache.Get(); Vector expectedRecoveryDetachment = innerCache.Get(); Vector avgDiscountFactor = innerCache.Get(); Vector pv = innerCache.Get(); // Get the expected loss and recovery for the tranche detachment and attachment parameters.ExpectedLossAndRecovery(expectedLossDetachment, expectedRecoveryDetachment, tgi.T, tPay, adjustedDetachment, realizedIndexLoss, realizedIndexRecovery); parameters.ExpectedLossAndRecovery(expectedLossAttachment, expectedRecoveryAttachment, tgi.T, tPay, adjustedAttachment, realizedIndexLoss, realizedIndexRecovery); expectedLoss.Assign((expectedLossDetachment - expectedLossAttachment) / trancheSize); expectedRecovery.Assign((expectedRecoveryDetachment - expectedRecoveryAttachment) / trancheSize); expectedWritedownPremiumNotional.Assign(expectedLoss + expectedRecovery); // Premium leg approximation: Accrued in default pays half the accrued. Remove expected loss and recovery (top down writeoff) premiumLeg.Assign(fDeal.Spread * (trancheRemainder - expectedWritedownPremiumNotional) * Accruals[i]); accruedInDefault.Assign(fDeal.Spread * (expectedWritedownPremiumNotional - oldExpectedWritedownPremiumNotional) * 0.5 * Accruals[i]); // Default leg approximation: account for default with bullet payment at end of period defaultLeg.Assign(expectedLoss - oldExpectedLoss); // Convention: bought CDO pays the premium to the buyer discountFactor.Assign(parameters.DF.Get(tgi.T, tPay)); avgDiscountFactor.Assign(0.5 * (discountFactor + oldDiscountFactor)); pv.Assign(scale * (premiumLeg * discountFactor + (accruedInDefault - defaultLeg) * avgDiscountFactor)); npv.Add(pv); if (accumulator != null && tgi.T == tPay) { accumulator.Accumulate(parameters.X, tgi.Date, scale * (premiumLeg + accruedInDefault - defaultLeg)); } if (needDiagnostics) { using (var innerCache1 = Vector.CacheLike(npv)) { Vector expectedPremium = innerCache1.Get(scale * premiumLeg); Vector expectedDefaultAccrual = innerCache1.Get(scale * accruedInDefault); Vector expectedDefaultLoss = innerCache1.Get(scale * defaultLeg); Vector pvDefaultAccrual = innerCache1.Get(expectedDefaultAccrual * avgDiscountFactor); Vector pvPremium = innerCache1.Get(expectedPremium * discountFactor); Vector pvProctection = innerCache1.Get(-expectedDefaultLoss * avgDiscountFactor); // accumulate sums if (i >= 0) { sumDefaultAccrual += pvDefaultAccrual[0]; sumPVPremium += pvPremium[0]; sumPVProtection += pvProctection[0]; } using (IntraValuationDiagnosticsHelper.StartCashflow(fIntraValuationDiagnosticsWriter, PayDates[i])) { var remainingPool = cache.Get(1.0 - realizedIndexLoss - realizedIndexRecovery); AddIntraValuationDiagnostics(fIntraValuationDiagnosticsWriter, parameters, adjustedAttachment, adjustedDetachment, remainingPool, tgi.T, tPay); IntraValuationDiagnosticsHelper.AddDetailedCDOCashflow(fIntraValuationDiagnosticsWriter, expectedPremium, expectedRecovery, expectedDefaultAccrual, expectedDefaultLoss, discountFactor, pv); } } } } return(LoopAction.Continue); }); if (needDiagnostics) { IntraValuationDiagnosticsHelper.AddSummaryCDOAmounts(fIntraValuationDiagnosticsWriter, npv, sumDefaultAccrual, sumPVPremium, sumPVProtection); } } result.AppendVector(tgi.Date, npv * parameters.X.Get(tgi.T)); } } }); } // After maturity result.Complete(fT); }
/// <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 cashAccumulators = valuationResults.Cash; BondOptionDeal deal = (BondOptionDeal)Deal; double baseDate = factors.BaseDate; double notional = deal.Notional; double interval = deal.Coupon_Interval; double buySign = (deal.Buy_Sell == BuySell.Buy) ? +1 : -1; double paySign = (deal.Option_Type == OptionType.Call) ? +1 : -1; double coupon = Percentage.PercentagePoint * deal.Coupon_Rate; double tExpiry = CalcUtils.DaysToYears(deal.Expiry_Date - baseDate); double tMaturity = CalcUtils.DaysToYears(deal.Bond_Maturity_Date - baseDate); IInterestYieldVol interestYieldVol = InterestVolBase.GetYieldVol(factors, deal.Yield_Volatility, fCurrency); if ((deal.Amortisation) != null && (deal.Amortisation.Count > 0)) { notional = deal.Amortisation.GetPrincipal(notional, deal.Expiry_Date); } bool respectDefault = Respect_Default == YesNo.Yes && fCreditRating != null; using (IntraValuationDiagnosticsHelper.StartDeal(fIntraValuationDiagnosticsWriter, Deal)) { using (var pricerCache = Vector.Cache(factors.NumScenarios)) { Vector defaultTime = null; Vector bondIsAlive = null; Vector historicalRecovery = null; if (respectDefault) { defaultTime = pricerCache.Get(); bondIsAlive = pricerCache.Get(1.0); historicalRecovery = pricerCache.GetClear(); fCreditRating.DefaultTime(defaultTime); } var defaultedBeforeBaseDate = respectDefault && CreditRating.DefaultedBeforeBaseDate(fCreditRating, baseDate); VectorEngine.For(tgi, () => { using (IntraValuationDiagnosticsHelper.StartValuation(fIntraValuationDiagnosticsWriter, tgi.Date)) { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector optionValue = cache.GetClear(); Vector stdDev = cache.Get(); // Std.Dev of Price Vector stdDevYield = cache.Get(); //Std.Dev of Yield Vector price = cache.Get(); Vector yield = cache.Get(); Vector macaulayDuration = cache.Get(); Vector bondValue = cache.Get(); Vector df = cache.Get(); Vector dfr = fRepoIsDiscount ? null : cache.Get(); if (defaultedBeforeBaseDate) { result.AppendVector(tgi.Date, optionValue); return(LoopAction.Break); } // This BondPrice function returns the value of the bond cashflows after ExpiryDate, including accrual, discounted back to T.date double accrual, cash; PricingFunctions.BondPrice(bondValue, out accrual, out cash, baseDate, tgi.Date, deal.Expiry_Date, deal.Issue_Date, deal.Bond_Maturity_Date, notional, coupon, fPayDates, fAccruals, fDiscountRate, deal.Amortisation, fPrincipals, fFinalPrincipal, fSurvivalProb, +1.0); // Now check scenario by scenario for defaults, overwriting bondValue as necessary if (respectDefault) { AdjustBondValueForDefault(notional, tExpiry, bondValue, bondIsAlive, historicalRecovery, defaultTime, tgi.T, fDiscountRate, fRecoveryRate); } // convert price and duration to forward (tExpiry) basis if (tgi.Date == deal.Expiry_Date) { optionValue.Assign(buySign * VectorMath.Max(0.0, paySign * (bondValue - notional * fStrike))); cashAccumulators.Accumulate(fFxRate, tgi.Date, optionValue); } else { fDiscountRate.GetValue(df, tgi.T, tExpiry); if (fRepoIsDiscount) { dfr = df; } else { fRepoRate.GetValue(dfr, tgi.T, tExpiry); } // Need yield and duration to convert yield vol to price vol. PricingFunctions.BondForwardPriceAndAdjustedMacaulayDuration(price, macaulayDuration, tgi.T, tExpiry, tMaturity, coupon, interval, df, fDiscountRate, fSurvivalProb); PricingFunctions.BondYieldFromPrice(yield, tExpiry, tMaturity, coupon, interval, price); // Calculate Modified Duration from Macaulay Duration. Vector modifiedDuration = cache.GetClear(); PricingFunctions.GetModifiedDuration(modifiedDuration, macaulayDuration, yield, interval); // Calculate Std.Dev of Yield and Price interestYieldVol.GetStdDev(stdDevYield, tgi.T, yield, fStrikeYield, tExpiry, tMaturity - tExpiry); stdDev.Assign(modifiedDuration * stdDevYield); if (interestYieldVol.GetDistributionType() == ProbabilityDistribution.Lognormal) { stdDev.MultiplyBy(yield); } price.AssignQuotient(bondValue, df); PricingFunctions.BlackFunction(optionValue, deal.Option_Type, price, notional * fStrike, stdDev); optionValue.MultiplyBy(buySign * dfr); if (fIntraValuationDiagnosticsWriter.Level > IntraValuationDiagnosticsLevel.None) { // Add Intra-valuation Diagnostics using (var volatilitiesAtDateStore = IntraValuationDiagnosticsHelper.CreateVolatilitiesAtDateStore(fIntraValuationDiagnosticsWriter, factors.NumScenarios)) using (var volatilitiesYieldAtDateStore = IntraValuationDiagnosticsHelper.CreateVolatilitiesAtDateStore(fIntraValuationDiagnosticsWriter, factors.NumScenarios)) { volatilitiesAtDateStore.Add(tgi.Date, tgi.TimeGrid.fEndDate, stdDev); volatilitiesYieldAtDateStore.Add(tgi.Date, tgi.TimeGrid.fEndDate, stdDevYield); IntraValuationDiagnosticsHelper.AddBondOptionProperties(fIntraValuationDiagnosticsWriter, price, dfr, bondValue, accrual, volatilitiesAtDateStore, volatilitiesYieldAtDateStore); IntraValuationDiagnosticsHelper.AddCashflowsPV(fIntraValuationDiagnosticsWriter, optionValue); } } } result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * optionValue); return(LoopAction.Continue); } } }); } result.Complete(fT); } }
/// <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); }
/// <summary> /// Value the deal. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); CallableBondForward deal = (CallableBondForward)Deal; double baseDate = factors.BaseDate; double settlementDate = deal.Settlement_Date; double tSettle = CalcUtils.DaysToYears(settlementDate - baseDate); TimeGridIterator tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; AccruedInterest accrued = valuationResults.Results <AccruedInterest>(); var intraValuationDiagnosticsWriter = IntraValuationDiagnosticsWriterFactory.GetOrCreate(IntraValuationDiagnosticsLevel.None); using (var outerCache = Vector.Cache(factors.NumScenarios)) { // SwapOptionPricerObject is null when there are no valid exercise dates. SwaptionPricer.WorkingArrays arrays = fSwaptionPricer != null?fSwaptionPricer.PreValue(fDiscountRate, outerCache) : null; Vector tExercise = outerCache.Get(double.PositiveInfinity); // time of exercise int numberScenariosExercised = 0; Vector defaultDate = fCreditRating != null?outerCache.Get(CalcUtils.DateTimeMaxValueAsDouble) : null; var defaultedBeforeBaseDate = fNeedsCreditRating && CreditRating.DefaultedBeforeBaseDate(fCreditRating, baseDate); VectorEngine.For(tgi, () => { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector cash = cache.GetClear(); Vector pv = cache.GetClear(); if (defaultedBeforeBaseDate || numberScenariosExercised == factors.NumScenarios) { // already defaulted before base date or All scenarios exercised result.AppendVector(tgi.Date, pv); return(LoopAction.Break); } else { // Value of the bond cashflows after the settlement. fCashflowList.Value(pv, cash, null, baseDate, tgi.Date, null, fDiscountRate, fSurvivalProb, null, null, intraValuationDiagnosticsWriter, 0.0); // Add the value of the principal and amortization cashflows. fFixedCashflowList.Value(pv, cash, baseDate, tgi.Date, null, fDiscountRate, fSurvivalProb, intraValuationDiagnosticsWriter, 0.0); if (fSurvivalProb != null) { fRecoveryList.Value(pv, baseDate, tgi.Date, fDiscountRate, fSurvivalProb, intraValuationDiagnosticsWriter); } if (fNeedsCreditRating) { UpdateDefaultDate(fCreditRating, tgi.Date, tgi.T, defaultDate); } // Add/subtract value of option if (fSwaptionPricer != null) { using (var innerCache = Vector.Cache(factors.NumScenarios)) { Vector optionPv = innerCache.Get(); Vector exerciseStrike = innerCache.GetClear(); // strike of underlying at exercise Vector exerciseFee = innerCache.GetClear(); // fee paid on exercise fSwaptionPricer.Value(optionPv, tgi.T, fDiscountRate, fInterestYieldVol, fSurvivalProb, arrays, tExercise, exerciseStrike, exerciseFee, Early_Exercise_Today == YesNo.Yes, ref numberScenariosExercised); // Ignore optionality if in default. if (fNeedsCreditRating) { optionPv.AssignConditional(defaultDate > tgi.Date, optionPv, 0.0); } pv.Add(optionPv); } } if (tgi.Date < settlementDate) { // Forward deal before settlement date if (deal.Is_Defaultable == YesNo.No) { pv.Assign((pv / fDiscountRate.Get(tgi.T, tSettle) - fSettlementAmount) * fRepoRate.Get(tgi.T, tSettle)); } else { pv.Subtract((fSettlementAmount - fAccrued) * fRepoRate.Get(tgi.T, tSettle) + fAccrued * fDiscountRate.Get(tgi.T, tSettle)); // discount accrued with bond rate; accrued interest must cancel } } else if (tgi.Date == settlementDate) { // Forward deal at settlement date pv.Subtract(fSettlementAmount); cash.Subtract(fSettlementAmount); } if (deal.IsForward()) { // Cash settled forward if (tgi.Date == settlementDate) { cash.Assign(pv); } else { cash.Clear(); } } else if (tgi.Date >= settlementDate) { using (var innerCache = Vector.Cache(factors.NumScenarios)) { Vector afterExercise = innerCache.Get(tExercise < tgi.T); Vector beforeExercise = innerCache.Get(tExercise > tgi.T); Vector exercisedToday = innerCache.GetClear(); exercisedToday.Assign(afterExercise.Or(beforeExercise)); exercisedToday.Assign(!exercisedToday); double callAmount = deal.Notional * Percentage.PercentagePoint * deal.Call_Prices.GetRate(tgi.Date); // Before exercise: pv is bondPV + optionPv and cash is bondCash. // On exercise: pv and cash are bondCash + callAmount. // After exercise: pv and cash are zero. cash.AssignConditional(exercisedToday, cash + callAmount, beforeExercise * cash); pv.AssignConditional(exercisedToday, cash, beforeExercise * pv); } } // Apply leg sign to results int buySellSign = deal.Buy_Sell == BuySell.Buy ? +1 : -1; ApplySign(pv, cash, buySellSign); if (fNeedsCreditRating) { Vector beforeExercise = cache.Get(tExercise > tgi.T); Vector modifiedDefaultDate = cache.Get(); // If default occurs after the call option has been exercise, default is irrelevant. // If default occurs on the same date that the call option is exercised, the assumption // is that the bond has been paid back in full, otherwise it wouldn''t be considered exercised. modifiedDefaultDate.AssignConditional(beforeExercise, defaultDate, double.PositiveInfinity); GetDefaultValue(baseDate, tgi.Date, modifiedDefaultDate, fRecoveryRate, pv, cash); } valuationResults.Cash.Accumulate(fFxRate, tgi.Date, cash); result.AppendVector(tgi.Date, pv * fFxRate.Get(tgi.T)); if (accrued != null) { accrued.SetValue(tgi.Date, fCashflowList.CalculateAccrual(tgi.Date, accrued.AccrueFromToday, fDeal.GetHolidayCalendar()) * buySellSign); } } } return(LoopAction.Continue); }); } result.Complete(fT); }
/// <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> /// Calculate valuation profiles. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); var result = valuationResults.Profile; var cashAccumulator = valuationResults.Cash; var accruedResults = valuationResults.Results <AccruedInterest>(); var deal = (IInflationCashflowListDeal)Deal; double sign = deal.Buy_Sell == BuySell.Buy ? 1.0 : -1.0; var cashflows = deal.GetCashflows(); var calendar = deal.GetHolidayCalendar(); double baseDate = factors.BaseDate; var tgi = new TimeGridIterator(fT); CalculateMetrics(valuationResults, factors, deal); using (IntraValuationDiagnosticsHelper.StartDeal(fIntraValuationDiagnosticsWriter, fDeal)) { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector defaultDate = null; if (!fIsDefaultNever) { defaultDate = fCreditRating != null?cache.Get(CalcUtils.DateTimeMaxValueAsDouble) : null; } Vector pv = cache.GetClear(); Vector cash = cashAccumulator.Ignore ? null : cache.Get(); Vector accruedInterest = cache.GetClear(); VectorEngine.For(tgi, () => { if (!fIsDefaultNever && CreditRating.DefaultedBeforeBaseDate(fCreditRating, baseDate)) { result.AppendVector(tgi.Date, pv); return(LoopAction.Break); } using (IntraValuationDiagnosticsHelper.StartCashflowsOnDate(fIntraValuationDiagnosticsWriter, tgi.Date)) { using (IntraValuationDiagnosticsHelper.StartCashflows(fIntraValuationDiagnosticsWriter, fFxRate, tgi.T, fDeal)) { cashflows.Value(pv, cash, baseDate, tgi.Date, deal.Settlement_Date, fInflationRate, fIndexVolatility, fDiscountRate, fRepoRate, fSurvivalProb, sign, fIntraValuationDiagnosticsWriter); IntraValuationDiagnosticsHelper.AddCashflowsPV(fIntraValuationDiagnosticsWriter, pv); if (fRecoveryCashflowList != null && fRecoveryCashflowList.Items.Count > 0) { fRecoveryCashflowList.Value(pv, cash, baseDate, tgi.Date, deal.Settlement_Date, fDiscountRate, fRepoRate, fInflationRate, fSurvivalProb, sign); } // Temporary fix up to avoid calculating default when we know the model doesn't support default if (!fIsDefaultNever) { UpdateDefaultDate(fCreditRating, tgi.Date, tgi.T, defaultDate); GetDefaultValue(baseDate, tgi.Date, defaultDate, fInflationRate, fIndexVolatility, fRepoRate, pv, cash); } result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * pv); if (cash != null) { cashAccumulator.Accumulate(fFxRate, tgi.Date, cash); } if (accruedResults != null) { cashflows.CalculateAccrual(accruedInterest, baseDate, tgi.Date, accruedResults.AccrueFromToday, calendar, fInflationRate, fIndexVolatility, sign); accruedResults.SetValue(tgi.Date, accruedInterest); } else if (fIntraValuationDiagnosticsWriter.Level > IntraValuationDiagnosticsLevel.None) { cashflows.CalculateAccrual(accruedInterest, baseDate, tgi.Date, false, calendar, fInflationRate, fIndexVolatility, sign); } IntraValuationDiagnosticsHelper.AddCashflowsAccruedInterest(fIntraValuationDiagnosticsWriter, accruedInterest); } } return(LoopAction.Continue); }); // On investment horizon or a bond forward's Settlement Date, the deal value is liquidated as cash. double endDate = Deal.EndDate(); if (cash != null && endDate <= fT.fHorizon) { // If endDate on a payment date, cashflow has already been accummulated (as cash), otherwise is 0. // Value liquidated is the value of the pv remaining after accummulating the cashflow. cash.AssignDifference(pv, cash); cashAccumulator.Accumulate(fFxRate, endDate, cash); } } result.Complete(fT); } }
private void SolveForYStar(Vector yStar, Vector[] coefficient, Vector[] coupon, Vector[] stdDev) { const int MaxIterations = 10; using (var cache = Vector.CacheLike(yStar)) { Vector y = cache.Get(); Vector value = cache.Get(); Vector deriv = cache.Get(); Vector factor = cache.Get(); Vector dy = cache.Get(); Vector product = cache.Get(); Vector numerator = cache.GetClear(); Vector demoninator = cache.GetClear(); int numberOfCoupons = coupon.Length; // Solve the equation to first order for the initial guess VectorEngine.For(0, numberOfCoupons, i => { product.Assign(coupon[i] * coefficient[i]); numerator.Add(product); demoninator.Add(product * stdDev[i]); return(LoopAction.Continue); }); y.Assign(numerator / demoninator); var dyAbs = cache.Get(); // Newton-Raphson loop to find yStar VectorEngine.For(0, MaxIterations, iteration => { value.Clear(); deriv.Clear(); VectorEngine.For(0, numberOfCoupons, i => { // Performs factor = c[i] * fCoefficient[i] * Exp(-stdDev[i] * y) factor.Assign(CalcUtils.SafeExpMultiply(-stdDev[i] * y, coupon[i] * coefficient[i])); value.Add(factor); deriv.Subtract(stdDev[i] * factor); return(LoopAction.Continue); }); // Could get divide by zero dy.Assign(value / deriv); // Terminate if solution reached. dyAbs.AssignAbs(dy); if (dyAbs.MaxElement() == 0) { return(LoopAction.Break); } y.Subtract(dy); return(LoopAction.Continue); }); yStar.Assign(y); } }
/// <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); }); } }