/// <summary> /// Modify the pv and cash taking the date of default into account. /// </summary> /// <remarks> /// The default value considers the buy/sell sign or whether it's a forward deal, but it doesn't consider whether the call option has been exercised or not. /// This is partly to respect the method signature of the base class, but partly the call exercise feature can be taken care of by modifying the actual /// underlier default date - if the name is in default but the call option has been exercised, the default has no consequence, and the "effective" /// default date can be moved to inifinity (as far as default value is concerned). /// </remarks> public override void GetDefaultValue(double baseDate, double valueDate, Vector defaultDate, RecoveryRate recoveryRate, Vector pv, Vector cash) { double principal = CFFixedInterestListValuation.GetPrincipal(fCashflowList, valueDate); var deal = (CallableBondForward)Deal; double settlementDate = deal.Settlement_Date; double t = CalcUtils.DaysToYears(valueDate - baseDate); double buySellSign = deal.Buy_Sell == BuySell.Buy ? 1.0 : -1.0; using (var cache = Vector.CacheLike(pv)) { // Approximation: recover only principal, neglecting accrued interest Vector recovery = cache.Get(buySellSign * 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 = buySellSign * 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> /// 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); } }