/// <summary> /// Prepare for valuation anything that is not dependent upon the scenario. /// </summary> public override void HeadNodeInitialize(PriceFactorList factors, BaseTimeGrid baseTimes, RequiredResults resultsRequired) { base.HeadNodeInitialize(factors, baseTimes, resultsRequired); var deal = (AverageForwardExplicitDealBase)Deal; fScale = (deal.Buy_Sell == BuySell.Buy ? +1 : -1) * deal.GetUnits(); fSamplingTimes = new double[deal.Sampling_Data.Count]; fSamplingTimesPlusTenor = new double[deal.Sampling_Data.Count]; int index = 0; Term tenorAsTerm = Period.ValueToTerm(deal.Tenor); // Loop over sampling dates and generate relevant sampling times. foreach (SamplingEntryAsset sample in deal.Sampling_Data) { double endDate = DateAdjuster.Add(sample.Date, tenorAsTerm, deal.GetHolidayCalendar()); double sampleTime = CalcUtils.DaysToYears(sample.Date - factors.BaseDate); // Store the start time and the end time. fSamplingTimes[index] = sampleTime; // Discount Factor and Forward Factor times are in Act365. fSamplingTimesPlusTenor[index] = CalcUtils.DaysToYears(endDate - factors.BaseDate); index++; } // Create a deep copy of the sampling data list and replace missing values with data from the rate fixings file var assetPrice = ((BaseAssetFxDealHelper)deal.GetDealHelper()).GetAssetPrice(factors); string assetCurrency = fPayoffType == PayoffType.Compo ? fPayoffCurrency : fCurrency; fSamplingData = deal.Sampling_Data.FillMissingDataFromFixings(factors.RateFixings, factors, assetPrice, assetCurrency, deal, "calculation of asset average"); // Add to valuation time grid fT.AddPayDate(deal.Maturity_Date, resultsRequired.CashRequired()); }
/// <summary> /// Calculate forward price, discount factor and volatility. /// </summary> protected override void PriceAndVolatility(double baseDate, double valueDate, Vector forwardPrice, Vector discountFactor, Vector volatility) { CommodityFutureOption deal = (CommodityFutureOption)Deal; double t = CalcUtils.DaysToYears(valueDate - baseDate); double tSettle = CalcUtils.DaysToYears(deal.Settlement_Date - baseDate); // Get spot price in contract currency forwardPrice.Assign(fCommodityPrice.Get(t) / fFxRate.Get(t)); if (volatility != null) { double tExpiry = CalcUtils.DaysToYears(deal.Expiry_Date - baseDate); if (tExpiry > t) { // temporary use of discountFactor vector to store strike discountFactor.Assign(deal.Strike); // Get volatility using spot price and strike volatility.Assign(fCommodityPriceVol.Get(t, forwardPrice, discountFactor, tExpiry)); } else { volatility.Clear(); } } fDiscountRate.GetValue(discountFactor, t, tSettle); // Get forward factor and complete calculation of forward price forwardPrice.MultiplyBy(fCommodityPrice.ForwardFactor(t, tSettle, fFxRate)); }
/// <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); }
/// <inheritdoc /> public override void HeadNodeInitialize(PriceFactorList factors, BaseTimeGrid baseTimes, RequiredResults requiredResults) { base.HeadNodeInitialize(factors, baseTimes, requiredResults); BaseCliquetOption deal = (BaseCliquetOption)fDeal; DateList accrualDates = CashflowGeneration.GenerateStripOfDates(deal.Effective_Date, deal.Maturity_Date, deal.Frequency, deal.GetHolidayCalendar()); fTimes = new double[accrualDates.Count]; for (int i = 0; i < accrualDates.Count; ++i) { fTimes[i] = CalcUtils.DaysToYears(accrualDates[i] - factors.BaseDate); } // Get the asset price from the deal helper var dealHelper = (BaseAssetFxDealHelper)deal.GetDealHelper(); IAssetPrice assetPrice = dealHelper.GetAssetPrice(factors); fKnownPrices = deal.GetKnownPrices(accrualDates, factors, assetPrice); // Add expiry dates to valuation time grid. if (accrualDates.Count > 1) { DateList expiryDates = new DateList(accrualDates); expiryDates.RemoveAt(0); fT.AddPayDates(expiryDates, requiredResults.CashRequired()); } }
/// <summary> /// Returns the payment amount on settlement date based on the cashflowlist. /// </summary> /// <remarks> /// The payment amount on settlement date will be either the dirty price of the bond or the clean price of the bond plus /// the accrued interest (two cashflows). Note that for a long position in a bond forward, this value is negative /// (indicating outgoing cashflow). /// </remarks> protected virtual void GetSettlementAmount(Vector amount, double valueDate, double baseDate, IInflationRate inflationRate, IPriceIndexVolatility indexVolatility) { amount.Clear(); using (var cache = Vector.CacheLike(amount)) { var deal = (IInflationCashflowListDeal)Deal; var cashflows = deal.GetCashflows(); Vector settlementPay = cache.Get(); for (int i = 0; i < cashflows.Count(); ++i) { if (cashflows.GetCashflow(i).Payment_Date < deal.Settlement_Date) { continue; } else if (cashflows.GetCashflow(i).Payment_Date == deal.Settlement_Date) { cashflows.GetCashflow(i).ExpectedAmount(settlementPay, CalcUtils.DaysToYears(valueDate - baseDate), inflationRate, indexVolatility, IntraValuationDiagnosticsWriterFactory.GetOrCreate(IntraValuationDiagnosticsLevel.None)); amount.Add(settlementPay); } else { break; } } } }
/// <summary> /// Generate CTD dates and set CTD coupon rate and conversion factor. /// </summary> protected void GenerateCTD(double baseDate, double issueDate, double maturityDate, double couponInterval, double firstCouponDate, double penultimateCouponDate, DayCount dayCount, IHolidayCalendar calendar, double couponRate, double conversionFactor) { if (conversionFactor <= 0.0) { return; // No CTD details or details invalid } BondFutureOption deal = (BondFutureOption)fDeal; // Validation of settlement date not done for CTD details on price factor if (deal.Settlement_Date >= maturityDate) { throw new AnalyticsException("Settlement date must be before cheapest-to-deliver maturity date."); } DateGenerationResults dateGenerationResults = deal.GetDateGenerationResults(issueDate, maturityDate, couponInterval, firstCouponDate, penultimateCouponDate, dayCount, calendar); fPayDates = dateGenerationResults.PayDates; fAccruals = dateGenerationResults.AccrualYearFractions; fIssueDate = issueDate; fMaturityDate = maturityDate; fCouponInterval = couponInterval; fCouponRate = couponRate; fConversionFactor = conversionFactor; fAccrual = PricingFunctions.AccruedInterest(deal.Settlement_Date, fIssueDate, fPayDates, fAccruals, fCouponRate, 1.0, null); double strike = PriceTransform(deal.Strike); double tSettle = CalcUtils.DaysToYears(deal.Settlement_Date - baseDate); double tMaturity = CalcUtils.DaysToYears(fMaturityDate - baseDate); fStrikeYield = PricingFunctions.BondYieldFromPrice(tSettle, tMaturity, couponRate, couponInterval, strike); }
/// <summary> /// Calculate forward price. /// </summary> protected override void ForwardPrice(double baseDate, double valueDate, Vector forwardPrice) { CommodityFuture deal = (CommodityFuture)Deal; double t = CalcUtils.DaysToYears(valueDate - baseDate); double tSettle = CalcUtils.DaysToYears(deal.Settlement_Date - baseDate); forwardPrice.Assign((fCommodityPrice.ForwardFactor(t, tSettle, fFxRate) * fCommodityPrice.Get(t)) / fFxRate.Get(t)); }
/// <summary> /// Modify the pv and cash taking the date of default into account. /// </summary> protected void GetDefaultValue(double baseDate, double valueDate, Vector defaultDate, IInflationRate inflationRate, IPriceIndexVolatility indexVolatility, IInterestRate repo, Vector pv, Vector cash) { IInflationCashflowListDeal deal = (IInflationCashflowListDeal)Deal; double settlementDate = deal.Settlement_Date; double t = CalcUtils.DaysToYears(valueDate - baseDate); double buySellSign = deal.Buy_Sell == BuySell.Buy ? 1.0 : -1.0; if (repo == null) { repo = fDiscountRate; } using (var cache = Vector.CacheLike(pv)) { Vector principal = cache.Get(); GetCurrentExposure(principal, t, valueDate, inflationRate); // Approximation: recover only principal, neglecting accrued interest. Vector recovery = cache.Get(buySellSign * principal * fRecoveryRate.Get(t)); if (valueDate <= settlementDate) { // Set the pv to (recovery - |settlementAmount|) * df when defaultDate <= valueDate <= settlementDate. // Set cash to (recovery - |settlementAmount|) when defaultDate <= valueDate = settlementDate (cash is always zero before settlementDate). // Note that GetSettlementAmount(...) will return a negative value for a long bond position, indicating an outgoing cashflow. double tSettle = CalcUtils.DaysToYears(settlementDate - baseDate); Vector settlementAmount = cache.Get(); GetSettlementAmount(settlementAmount, valueDate, baseDate, inflationRate, indexVolatility); settlementAmount.MultiplyBy(buySellSign); Vector hasDefaulted = cache.Get(defaultDate <= valueDate); pv.AssignConditional(hasDefaulted, repo.Get(t, tSettle) * (recovery + settlementAmount), pv); if (cash != null && valueDate == settlementDate) { cash.AssignConditional(hasDefaulted, pv, cash); } } else { // after settlement date recovery.MultiplyBy(defaultDate >= valueDate); // set to zero if already defaulted Vector notDefaulted = cache.Get(defaultDate > valueDate); pv.AssignConditional(notDefaulted, pv, recovery); if (cash != null) { cash.AssignConditional(notDefaulted, cash, recovery); } } } }
private void ValueOnOrAfterExpiry(Vector pv, Vector exerciseWeight, Vector settlementCash, Vector cash, PriceFactorList factors, bool isCashSettled, bool isPhysicallySettled, bool cashRequired, TimeGridIterator tgi, double paySign) { double baseDate = factors.BaseDate; double tSettlement = isCashSettled ? CalcUtils.DaysToYears(fSwaptionDeal.Settlement_Date - baseDate) : 0.0; using (var cache = Vector.Cache(factors.NumScenarios)) { Vector fixedPv = cache.Get(); Vector fixedCash = cache.Get(); Vector floatPv = cache.Get(); Vector floatCash = cache.Get(); ValueFixedAndFloatingLegs(tgi, fixedPv, fixedCash, baseDate, floatPv, floatCash); if (tgi.Date == fSwaptionDeal.Option_Expiry_Date) { pv.Assign(VectorMath.Max(0.0, paySign * (floatPv - fixedPv))); if (isPhysicallySettled) { exerciseWeight.Assign(pv > 0.0); // 1 if exercised and otherwise 0 } else if (isCashSettled) { settlementCash.Assign(pv); // Records cash settlement amount on Option Expiry Date. pv.Assign(settlementCash * fDiscountRate.Get(tgi.T, tSettlement)); // Factor in time value of money due to settlement delay. } } else { // After expiry if (isPhysicallySettled) { pv.Assign(paySign * (floatPv - fixedPv) * exerciseWeight); if (cashRequired) { cash.Assign(paySign * (floatCash - fixedCash) * exerciseWeight); } } else if (isCashSettled) { pv.Assign(settlementCash * fDiscountRate.Get(tgi.T, tSettlement)); if (tgi.Date == fSwaptionDeal.Settlement_Date && cashRequired) { cash.Assign(settlementCash); } } } } }
/// <summary> /// Value the swaption before expiry using a Jamshidian style decomposition to value the swaption analytically. /// Checks for the validity of the decomposition and falls back on numerical integration when necessary. /// </summary> private void ValueBeforeExpiry(Vector pv, PriceFactorList factors, bool isCashSettled, TimeGridIterator tgi) { double baseDate = factors.BaseDate; double tExpiry = fSwaptionDeal.GetTimeToExpiry(baseDate); double tValue = CalcUtils.DaysToYears(tgi.Date - baseDate); int count = fDates.Count; using (var cache = Vector.Cache(factors.NumScenarios)) { Vector dfTExpiry = cache.Get(); Vector yStar = cache.Get(); Vector isUnique = cache.Get(); Vector analyticPv = cache.GetClear(); Vector numericalPv = cache.GetClear(); // Theory guide mappings for the vector arrays are: // df <=> D(0, T_i) // coupons <=> C_i // stdDev <=> v_i // coefficient <=> D(0, T_i)/D(0, T) exp (- v_i^2 / 2) Vector[] df = new Vector[count]; Vector[] coupons = new Vector[count]; Vector[] stdDev = new Vector[count]; Vector[] coefficient = new Vector[count]; CreateVectorArrays(df, cache, coupons, coefficient, stdDev); fDiscountRate.GetValue(dfTExpiry, tValue, tExpiry); GetSwapQuantities(coupons, stdDev, coefficient, df, tValue, tExpiry, baseDate, dfTExpiry); // Find the value of y which makes the underlying swap value to 0, used for Jamshidian style decomposition SolveForYStar(yStar, coefficient, coupons, stdDev); // Calculate the scenarios in which the yStar values are guaranteed unique IsSolutionUnique(isUnique, yStar, coefficient, coupons, stdDev); CalculateAnalyticPV(analyticPv, isUnique, stdDev, yStar, coefficient, coupons, dfTExpiry, df); CalculateNumericalPV(numericalPv, isUnique, stdDev, coefficient, coupons); pv.AssignConditional(isUnique >= 1.0, analyticPv, numericalPv); if (isCashSettled) { double tSettlement = CalcUtils.DaysToYears(fSwaptionDeal.Settlement_Date - baseDate); // Factor in time value of money due to settlement delay. pv.MultiplyBy(fDiscountRate.Get(tgi.T, tSettlement) / fDiscountRate.Get(tgi.T, tExpiry)); } } }
/// <summary> /// This method Adjusts the bond price vector by taking the default into account give the default time vector. /// </summary> public static void AdjustForDefault(double baseDate, double valueDate, Vector bondPrice, TDate expiryDate, bool respectDefault, Vector underlyingIsAlive, Vector historicalRecovery, Vector defaultTime, IInterestRate discountRate, RecoveryRate recoveryRate) { // Adjust the bond price for default: check scenario by scenario for defaults, overwriting bondPrice as necessary if (!respectDefault) { return; } double currentTime = CalcUtils.DaysToYears(valueDate - baseDate); double tExpiry = CalcUtils.DaysToYears(expiryDate - baseDate); BondOptionValuation.AdjustBondValueForDefault(1.0, tExpiry, bondPrice, underlyingIsAlive, historicalRecovery, defaultTime, currentTime, discountRate, recoveryRate); }
/// <summary> /// Modify the pv and cash taking the date of default into account. /// </summary> public override void GetDefaultValue(double baseDate, double valueDate, Vector defaultDate, RecoveryRate recoveryRate, Vector pv, Vector cash) { var deal = (CFFloatingInterestListDeal)Deal; double principal = GetPrincipal(fCashflows, valueDate); double settlementDate = deal.Settlement_Date; double t = CalcUtils.DaysToYears(valueDate - baseDate); using (var cache = Vector.CacheLike(pv)) { // Approximation: recover only principal, neglecting accrued interest Vector recovery = cache.Get(fBuySellSign * principal * recoveryRate.Get(t)); if (valueDate <= settlementDate) { // Set the pv to (recovery - settlementAmount) * df when defaultDate <= valueDate <= settlementDate. // Set cash to (recovery - settlementAmount) when defaultDate <= valueDate = settlementDate (cash is always zero before settlementDate). double tSettle = CalcUtils.DaysToYears(settlementDate - baseDate); Vector settlementAmount = cache.GetClear(); if (deal.Settlement_Amount_Is_Clean == YesNo.Yes) { fCashflows.CalculateAccrual(settlementAmount, baseDate, settlementDate, false, deal.AccrualHolidayCalendar(), fForecastRate); } settlementAmount.Add(deal.Settlement_Amount); Vector hasDefaulted = cache.Get(defaultDate <= valueDate); pv.AssignConditional(hasDefaulted, fRepoRate.Get(t, tSettle) * (recovery - fBuySellSign * settlementAmount), pv); if (cash != null && valueDate == settlementDate) { cash.AssignConditional(hasDefaulted, pv, cash); } } else { // after settlement date recovery.MultiplyBy(defaultDate >= valueDate); // set to zero if already defaulted Vector notDefaulted = cache.Get(defaultDate > valueDate); pv.AssignConditional(notDefaulted, pv, recovery); if (cash != null) { cash.AssignConditional(notDefaulted, cash, recovery); } } } }
/// <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); }
/// <inheritdoc /> protected override void ForwardPrice(double baseDate, double valueDate, Vector forwardPrice) { BondFuture deal = (BondFuture)fDeal; double t = CalcUtils.DaysToYears(valueDate - baseDate); double tSettle = CalcUtils.DaysToYears(deal.Settlement_Date - baseDate); double accrual, cash; PricingFunctions.BondPrice(forwardPrice, out accrual, out cash, baseDate, valueDate, deal.Settlement_Date, fIssueDate, fMaturityDate, 1.0, fCouponRate, fPayDates, fAccruals, fDiscountRate, null, null, 0.0, fSurvivalProb, 1.0); AdjustForDefault(baseDate, valueDate, forwardPrice, deal.Expiry_Date, Respect_Default == YesNo.Yes && !string.IsNullOrEmpty(deal.Issuer), fUnderlyingIsAlive, fHistoricalRecovery, fDefaultTime, fDiscountRate, fRecoveryRate); // If deal.Repo_Rate is null or empty then fRepoRate will default to the DiscountRate forwardPrice.Assign(Percentage.OverPercentagePoint * ((forwardPrice / fRepoRate.Get(t, tSettle) - accrual)) / fConversionFactor); }
/// <summary> /// Prepare for valuation anything that will be shared between scenarios. /// </summary> public override void PreCloneInitialize(PriceFactorList factors, BaseTimeGrid baseTimes, RequiredResults requiredResults) { base.PreCloneInitialize(factors, baseTimes, requiredResults); var deal = (BondOptionDeal)Deal; var tExpiry = deal.GetTimeToExpiry(factors.BaseDate); var tMaturity = CalcUtils.DaysToYears(deal.Bond_Maturity_Date - factors.BaseDate); var dateGenerationResults = deal.GetDateGenerationResults(); fPayDates = dateGenerationResults.PayDates; fAccruals = dateGenerationResults.AccrualYearFractions; fPrincipals = dateGenerationResults.Principals; fFinalPrincipal = dateGenerationResults.FinalPrincipal; fStrike = deal.Strike_Price; fStrikeYield = deal.GetStrikeYield(ref fStrike, tExpiry, tMaturity, fPayDates, fAccruals); fT.AddPayDate(deal.Expiry_Date, requiredResults.CashRequired()); }
/// <summary> /// Single date valuation function. /// </summary> public void Value(Vector pv, Vector cash, double baseDate, double valueDate, Vector settlementDate, IInterestRate discount, IInterestRate forecast, IInterestRate repo, IInterestRateVol interestRateVol, IInterestYieldVol interestYieldVol, ISurvivalProb survivalProb, ISACCRResult saccrResult, IIntraValuationDiagnosticsWriter intraValuationDiagnosticsWriter) { FixedCashflowBaseDeal deal = (FixedCashflowBaseDeal)fDeal; double payDate = deal.Payment_Date; if (payDate < valueDate) { return; } using (var cache = Vector.CacheLike(pv)) { Vector amount = cache.Get(fAmount); if (settlementDate != null) { amount.MultiplyBy(settlementDate < payDate); } if (payDate == valueDate) { pv.Assign(amount); if (cash != null) { cash.Assign(amount); } } else { double t = CalcUtils.DaysToYears(valueDate - baseDate); double tPay = CalcUtils.DaysToYears(payDate - baseDate); if (survivalProb != null) { pv.Assign(amount * discount.Get(t, tPay) * survivalProb.Get(t, tPay)); } else { pv.Assign(amount * discount.Get(t, tPay)); } } } }
/// <summary> /// Modify the pv and cash taking the date of default into account. /// </summary> public override void GetDefaultValue(double baseDate, double valueDate, Vector defaultDate, RecoveryRate recoveryRate, Vector pv, Vector cash) { CFFixedInterestListDeal deal = (CFFixedInterestListDeal)fDeal; double principal = GetPrincipal(deal.Cashflows, valueDate); double settlementDate = deal.Settlement_Date; double t = CalcUtils.DaysToYears(valueDate - baseDate); using (var cache = Vector.CacheLike(pv)) { // Approximation: recover only principal, neglecting accrued interest Vector recovery = cache.Get(fBuySellSign * principal * recoveryRate.Get(t)); if (valueDate <= settlementDate) { // Set the pv to (recovery - settlementAmount) * df when defaultDate <= valueDate <= settlementDate. // Set cash to (recovery - settlementAmount) when defaultDate <= valueDate = settlementDate (cash is always zero before settlementDate). double tSettle = CalcUtils.DaysToYears(settlementDate - baseDate); double settlementAmount = fBuySellSign * fSettlementAmount; Vector hasDefaulted = cache.Get(defaultDate <= valueDate); pv.AssignConditional(hasDefaulted, fRepoRate.Get(t, tSettle) * (recovery - settlementAmount), pv); if (cash != null && valueDate == settlementDate) { cash.AssignConditional(hasDefaulted, pv, cash); } } else { // after settlement date recovery.MultiplyBy(defaultDate >= valueDate); // set to zero if already defaulted Vector notDefaulted = cache.Get(defaultDate > valueDate); pv.AssignConditional(notDefaulted, pv, recovery); if (cash != null) { cash.AssignConditional(notDefaulted, cash, recovery); } } } }
/// <summary> /// Validations. /// </summary> public override void Validate(ICalendarData calendar, ErrorList errors) { base.Validate(calendar, errors); CalcUtils.ValidateDates(errors, Effective_Date, Maturity_Date, true); // pay frequency (period) must not be greater than entire swap tenor double swapTenor = CalcUtils.DaysToYears(fMaturityDate - fEffectiveDate); if (fPayFrequency > swapTenor) { AddToErrors(errors, "Period between payments cannot be greater than period from Effective Date to Maturity Date (pay side)"); } // attachment must be on [0,1), detachment on (0,1], attachment < detachment if ((fAttachment < 0) || (fAttachment >= 1)) { AddToErrors(errors, "Attachment point must be less than one and cannot be less than zero"); } if ((fDetachment <= 0) || (fDetachment > 1)) { AddToErrors(errors, "Detachment point must be greater than zero and cannot be greater than one"); } if (fDetachment <= fAttachment) { AddToErrors(errors, "Attachment point must be less than detachment point"); } if (Upfront_Amount != 0 && Upfront_Date == 0.0) { AddToErrors(errors, "Upfront date must be specified if there is an upfront payment"); } if (string.IsNullOrEmpty(Reference_Index)) { AddToErrors(errors, "Reference Index must be specified"); } }
/// <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> /// 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> /// 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> /// Value the deal using the cashflow list. /// </summary> /// <param name="pv">Present value to be updated.</param> /// <param name="cash">Realised cash to be updated.</param> public void Value(Vector pv, Vector cash, double baseDate, double valueDate, Vector settlementDate, IInterestRate discount, IInterestRate forecast, IInterestRate repo, IInterestRateVol interestRateVol, IInterestYieldVol interestYieldVol, ISurvivalProb survivalProb, ISACCRResult saccrResult, IIntraValuationDiagnosticsWriter intraValuationDiagnosticsWriter) { CFFixedInterestListDeal deal = (CFFixedInterestListDeal)Deal; pv.Clear(); if (cash != null) { cash.Clear(); } deal.Cashflows.Value(pv, cash, null, baseDate, valueDate, settlementDate, discount, survivalProb, fFxRate, fRateFxRate, intraValuationDiagnosticsWriter, fCutoffDate); using (var cache = Vector.CacheLike(pv)) { Vector sp = cache.Get(1.0); double dealSettlementDate = deal.Settlement_Date; double t = CalcUtils.DaysToYears(valueDate - baseDate); double tSettle = CalcUtils.DaysToYears(dealSettlementDate - baseDate); if (Use_Survival_Probability == YesNo.Yes && survivalProb != null) { survivalProb.GetValue(sp, t, tSettle); fRecoveryList.Value(pv, baseDate, valueDate, discount, survivalProb, intraValuationDiagnosticsWriter); } if (valueDate < dealSettlementDate) { // Forward deal before settlement date if (deal.Is_Defaultable == YesNo.No) { pv.Assign((pv / discount.Get(t, tSettle) - fSettlementAmount) * repo.Get(t, tSettle)); } else { pv.Subtract(fAccruedInterest * discount.Get(t, tSettle) * sp + (fSettlementAmount - fAccruedInterest) * repo.Get(t, tSettle)); } } else if (valueDate == dealSettlementDate) { // Forward deal at settlement date pv.Subtract(fSettlementAmount); if (cash != null) { if (deal.Settlement_Style == SettlementType.Cash) { cash.Assign(pv); } else { cash.Subtract(fSettlementAmount); } } } } pv.AssignProduct(fBuySellSign, pv); if (cash != null) { cash.AssignProduct(fBuySellSign, cash); } }
/// <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> /// 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> /// Calculates time to expiry for a given baseDate. /// </summary> public double GetTimeToExpiry(double baseDate) { return(CalcUtils.DaysToYears(Expiry_Date - baseDate)); }
/// <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 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> /// Calculate forward price, discount factor and volatility /// </summary> protected override void PriceAndVolatility(double baseDate, double valueDate, Vector forwardPrice, Vector discountFactor, Vector volatility) { BondFutureOption deal = (BondFutureOption)fDeal; double t = CalcUtils.DaysToYears(valueDate - baseDate); double tSettle = CalcUtils.DaysToYears(deal.Settlement_Date - baseDate); double tMaturity = CalcUtils.DaysToYears(fMaturityDate - baseDate); fDiscountRate.GetValue(discountFactor, t, tSettle); if (volatility != null) { double tExpiry = deal.GetTimeToExpiry(baseDate); if (tExpiry > t) { // Calculate price volatility using (var cache = Vector.CacheLike(forwardPrice)) { Vector macaulayDuration = cache.Get(); Vector yield = cache.Get(); Vector yieldStrike = cache.Get(fStrikeYield); // Calculate forwrad price, yield and adjusted duration using simple bond price functions PricingFunctions.BondForwardPriceAndAdjustedMacaulayDuration(forwardPrice, macaulayDuration, t, tSettle, tMaturity, fCouponRate, fCouponInterval, discountFactor, fDiscountRate, fSurvivalProb); PricingFunctions.BondYieldFromPrice(yield, tSettle, tMaturity, fCouponRate, fCouponInterval, forwardPrice); // Calculate Modified Duration from Macaulay Duration. Vector modifiedDuration = cache.Get(); PricingFunctions.GetModifiedDuration(modifiedDuration, macaulayDuration, yield, fCouponInterval); // Get yield volatility fInterestYieldVol.GetValue(volatility, t, yield, yieldStrike, tExpiry, tMaturity - tSettle); // Convert (normal) yield vol to lognormal price vol volatility.MultiplyBy(modifiedDuration); if (fInterestYieldVol.GetDistributionType() == ProbabilityDistribution.Lognormal) { // Convert lognormal yield vol to lognormal price vol. volatility.MultiplyBy(yield); } } } else { volatility.Clear(); } } // Recalculate forward price using fAccruals double accrual, cash; PricingFunctions.BondPrice(forwardPrice, out accrual, out cash, baseDate, valueDate, deal.Settlement_Date, fIssueDate, fMaturityDate, 1.0, fCouponRate, fPayDates, fAccruals, fDiscountRate, null, null, 0.0, fSurvivalProb, 1.0); BondFutureValuation.AdjustForDefault(baseDate, valueDate, forwardPrice, deal.Expiry_Date, Respect_Default == YesNo.Yes && !string.IsNullOrEmpty(deal.Issuer), fUnderlyingIsAlive, fHistoricalRecovery, fDefaultTime, fDiscountRate, fRecoveryRate); forwardPrice.AssignQuotient(forwardPrice, discountFactor); if (!fRepoIsDiscount) { fRepoRate.GetValue(discountFactor, t, tSettle); } }
/// <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); }