/// <summary> /// Prepare for valuation anything that is dependent upon the scenario. /// </summary> public override void PreValue(PriceFactorList factors) { base.PreValue(factors); CallableBondForward deal = (CallableBondForward)Deal; fInterestYieldVol = InterestVolBase.GetYieldVol(factors, deal.Yield_Volatility, fCurrency); fNeedsCreditRating = NeedCreditRating(); fCreditRating = NeedCreditRating() ? factors.Get <CreditRating>(deal.Issuer) : null; fRecoveryRate = NeedRecovery() ? factors.Get <RecoveryRate>(string.IsNullOrEmpty(deal.Recovery_Rate) ? deal.Issuer : deal.Recovery_Rate) : null; fSurvivalProb = NeedSurvivalProb() ? factors.GetInterface <ISurvivalProb>(string.IsNullOrEmpty(deal.Survival_Probability) ? deal.Issuer : deal.Survival_Probability) : null; }
/// <summary> /// Register price factors. /// </summary> public override void RegisterFactors(PriceFactorList factors, ErrorList errors) { base.RegisterFactors(factors, errors); CallableBondForward deal = (CallableBondForward)Deal; InterestVolBase.RegisterInterestYieldVol(factors, deal.Yield_Volatility, deal.Currency); if (NeedCreditRating()) { factors.Register <CreditRating>(deal.Issuer); } if (NeedRecovery()) { factors.Register <RecoveryRate>(string.IsNullOrEmpty(deal.Recovery_Rate) ? deal.Issuer : deal.Recovery_Rate); } if (NeedSurvivalProb()) { factors.RegisterInterface <ISurvivalProb>(string.IsNullOrEmpty(deal.Survival_Probability) ? deal.Issuer : deal.Survival_Probability); } }
/// <summary> /// True if the Issuer field on the deal is specified; false otherwise. /// </summary> private bool DealHasIssuer() { CallableBondForward deal = (CallableBondForward)Deal; return(!string.IsNullOrEmpty(deal.Issuer)); }
/// <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> /// Prepare for valuation anything that is not dependent upon the scenario. /// </summary> public override void PreCloneInitialize(PriceFactorList factors, BaseTimeGrid baseTimes, RequiredResults requiredResults) { base.PreCloneInitialize(factors, baseTimes, requiredResults); CallableBondForward deal = (CallableBondForward)Deal; double firstCallDate = deal.First_Call_Date; double lastCallDate = deal.Last_Call_Date; double baseDate = factors.BaseDate; double issueDate = deal.Issue_Date; double settlementDate = deal.Settlement_Date; double priceDate = Math.Max(baseDate, settlementDate + 1.0); // bond cashflows before priceDate do not contribute to bond price double maturityDate = deal.Bond_Maturity_Date; double couponInterval = deal.Coupon_Interval; double notional = deal.Notional; IHolidayCalendar holidayCalendar = deal.GetHolidayCalendar(); DateGenerationParams dateGenerationParams = new DateGenerationParams { EffectiveDate = issueDate, MaturityDate = maturityDate, AccrualDayCount = deal.Accrual_Day_Count, FirstCouponDate = deal.First_Coupon_Date, PenultimateCouponDate = deal.Penultimate_Coupon_Date, Amortisation = deal.Amortisation, CouponPeriod = couponInterval, Principal = notional, PrincipalExchange = PrincipalExchange.Start_Maturity, AccrualCalendar = holidayCalendar }; CashflowListDetail detail = CashflowGeneration.GenerateCashflowListDetail(dateGenerationParams); // Collect reset dates as we loop. var resetDates = new DateList(detail.Coupon_Details.Count); // Create cashflow list fCashflowList = new CFFixedInterestList(); fCashflowList.Compounding = YesNo.No; foreach (CouponDetail couponDetail in detail.Coupon_Details) { if (couponDetail.Payment_Date < priceDate) { continue; } foreach (AccrualDetail accrualDetail in couponDetail.Accrual_Details) { resetDates.Add(accrualDetail.Accrual_Start_Date); if (couponDetail.Payment_Date < priceDate) { continue; } var cashflow = new CFFixedInterest { Payment_Date = couponDetail.Payment_Date, Notional = accrualDetail.Notional, Accrual_Start_Date = accrualDetail.Accrual_Start_Date, Accrual_End_Date = accrualDetail.Accrual_End_Date, Accrual_Year_Fraction = accrualDetail.Accrual_Year_Fraction, Rate = deal.Coupon_Rate * Percentage.PercentagePoint, Accrual_Day_Count = deal.Accrual_Day_Count, Discounted = YesNo.No }; fCashflowList.Items.Add(cashflow); } } IRBaseDealSkin.ApplyRateSchedule(fCashflowList.Items, deal.Coupon_Rate_Schedule, Percentage.PercentagePoint, holidayCalendar, DateAdjustmentMethod.Modified_Following); // Calculate fixed interest cashflows. fCashflowList.CalculateInterest(baseDate); fFixedCashflowList = PrincipalCashflows(priceDate, issueDate, maturityDate, PrincipalExchange.Start_Maturity, notional, deal.Amortisation, 1.0); fSettlementAmount = 0.0; fAccrued = 0.0; bool payDatesRequired = requiredResults.CashRequired(); if (settlementDate >= baseDate) { double settlementPrincipal = CFFixedInterestListValuation.GetPrincipal(fCashflowList, settlementDate); fSettlementAmount = settlementPrincipal * deal.Price * Percentage.PercentagePoint; for (int i = 0; i < fCashflowList.Items.Count; ++i) { CFFixedInterest cashflow = fCashflowList[i]; if (cashflow.Accrual_Start_Date >= settlementDate) { break; } if (settlementDate < cashflow.Accrual_End_Date) { fAccrued += cashflow.Interest() * (settlementDate - cashflow.Accrual_Start_Date) / (cashflow.Accrual_End_Date - cashflow.Accrual_Start_Date); } } if (deal.Price_Is_Clean == YesNo.Yes) { fSettlementAmount += fAccrued; // add accrued interest } fT.AddPayDate(settlementDate, payDatesRequired); } // Add the floating and fixed cashflow dates to the time grid. fT.AddPayDates <CFFixedInterest>(fCashflowList, payDatesRequired); fT.AddPayDates <CFFixed>(fFixedCashflowList, payDatesRequired); // We only need an option pricer if callable on or after the settlement date. fSwaptionPricer = null; if (lastCallDate >= settlementDate) { // Snap call dates to grid of reset dates and // ensure that first call date is on or after settlement date int iLast = resetDates.IndexOfNextDate(lastCallDate); lastCallDate = resetDates[iLast]; int iFirst = resetDates.IndexOfNextDate(firstCallDate); while ((iFirst < resetDates.Count - 1) && (resetDates[iFirst] < settlementDate)) { // move first exercise date forward iFirst++; } firstCallDate = resetDates[iFirst]; int paySign = deal.Call_Put == OptionType.Put ? +1 : -1; RateList exerciseFees = new RateList(); foreach (Rate price in deal.Call_Prices) { Rate fee = new Rate(); fee.Value = paySign * (Percentage.OverPercentagePoint - price.Value); fee.Date = price.Date; exerciseFees.Add(fee); } var amortisation = AllocateAmortisationToPaymentDates <CFFixedInterest>(deal.Amortisation, fCashflowList.Items); fSwaptionPricer = new SwaptionPricer(issueDate, maturityDate, couponInterval, couponInterval, deal.Accrual_Day_Count, holidayCalendar, DayCount.ACT_365, holidayCalendar, firstCallDate, lastCallDate, baseDate, paySign, paySign, 0.0, null, notional, amortisation, deal.Coupon_Rate, null, deal.Coupon_Rate_Schedule, exerciseFees, null, OptionStyle2.Bermudan, Max_Nodes, Step_Size, fT, true, requiredResults.CashRequired()); } if (NeedSurvivalProb()) { fRecoveryList = new CFRecoveryList(); fRecoveryList.PopulateRecoveryCashflowList(baseDate, settlementDate, fCashflowList); } }