public Cashflow[] GetCashflows(IMarketCondition market, bool netted = true) { //var payTerm = Frequency.GetTerm(); //var accDates = new List<Date> {StartDate}; //accDates.AddRange(new Schedule(FirstPaymentDate, MaturityDate, payTerm, Stub.ShortEnd).ToArray()) //var Accruals = new Schedule(_implicitAccStartDate, MaturityDate, payTerm, Stub.ShortEnd).ToArray(); var len = Accruals.Length; var tArray = new double[len - 1]; for (var i = 0; i < len - 1; ++i) { tArray[i] = DayCount.CalcDayCountFraction(Accruals[i], Accruals[i + 1]); } double[] begPrincipal; double[] interest; double[] principalPay; double[] prepayment; double[] defaultPrincipal; if (!IsFloatingRate) { MortgageCalculator.GetPaymentDetails(tArray, Notional, Coupon, Frequency, AmortizationType, out begPrincipal, out interest, out principalPay, out prepayment, out defaultPrincipal, _numPastPayment); var beg = Accruals[0]; var end = Accruals[1]; if (StartDate != beg) { //first interest should be adjusted interest[0] = interest[0] * (end - StartDate) / (end - beg); } } else { double[] begPrincipal1; double[] interest1; double[] principalPay1; double[] prepayment1; double[] defaultPrincipal1; MortgageCalculator.GetPaymentDetails(tArray, Notional, Coupon, Frequency, AmortizationType, out begPrincipal1, out interest1, out principalPay1, out prepayment1, out defaultPrincipal1, _numPastPayment); var ratePairs = market.HistoricalIndexRates.Value[IndexType].Select(x => Tuple.Create(x.Key, x.Value)).OrderBy(x => x.Item1).ToArray(); int index; for (index = ratePairs.Length - 1; index > 0; --index) { if (ratePairs[index].Item1 <= ResetDate) { break; } } var newRate = ratePairs[index].Item2 * FloatingRateMultiplier; double[] begPrincipal2; double[] interest2; double[] principalPay2; double[] prepayment2; double[] defaultPrincipal2; var n = Accruals.FirstIndexOf(x => x > ResetDate); var p = n < 2 ? Notional : begPrincipal1[n - 2]; MortgageCalculator.GetPaymentDetails(tArray.Skip(n - 1).ToArray(), p, newRate, Frequency, AmortizationType, out begPrincipal2, out interest2, out principalPay2, out prepayment2, out defaultPrincipal2, _numPastPayment); //accDate[n-1] accDate[n] var start = Accruals[n - 1]; var end = Accruals[n]; var newInterst = p * (DayCount.CalcDayCountFraction(start, ResetDate) * Coupon + DayCount.CalcDayCountFraction(ResetDate, end) * newRate); interest1[n - 1] = newInterst; principalPay1[n - 1] = principalPay2[0]; prepayment1[n - 1] = prepayment2[0]; defaultPrincipal1[n - 1] = defaultPrincipal2[0]; begPrincipal1[n - 1] = p - principalPay1[n - 1] - prepayment1[n - 1] - defaultPrincipal1[n - 1]; double[] begPrincipal3; double[] interest3; double[] principalPay3; double[] prepayment3; double[] defaultPrincipal3; MortgageCalculator.GetPaymentDetails(tArray.Skip(n).ToArray(), begPrincipal1[n - 1], newRate, Frequency, AmortizationType, out begPrincipal3, out interest3, out principalPay3, out prepayment3, out defaultPrincipal3, _numPastPayment); var beg = Accruals[0]; end = Accruals[1]; if (StartDate != beg) { //first interest should be adjusted interest1[0] = interest1[0] * (end - StartDate) / (end - beg); } var tempList = begPrincipal1.Take(n).ToList(); tempList.AddRange(begPrincipal3); begPrincipal = tempList.ToArray(); tempList = interest1.Take(n).ToList(); tempList.AddRange(interest3); interest = tempList.ToArray(); tempList = principalPay1.Take(n).ToList(); tempList.AddRange(principalPay3); principalPay = tempList.ToArray(); tempList = prepayment1.Take(n).ToList(); tempList.AddRange(prepayment3); prepayment = tempList.ToArray(); tempList = defaultPrincipal1.Take(n).ToList(); tempList.AddRange(defaultPrincipal3); defaultPrincipal = tempList.ToArray(); } var cashflows = new List <Cashflow>(); for (var i = 0; i < begPrincipal.Length; ++i) { var df = market.GetDf(Accruals[i + 1]); cashflows.Add(new Cashflow(Accruals[i], Accruals[i + 1], Accruals[i + 1], principalPay[i], Currency, CashflowType.Principal, true, df, null)); cashflows.Add(new Cashflow(Accruals[i], Accruals[i + 1], Accruals[i + 1], interest[i], Currency, CashflowType.Coupon, true, df, null)); cashflows.Add(new Cashflow(Accruals[i], Accruals[i + 1], Accruals[i + 1], -interest[i] * TaxRate, Currency, CashflowType.Tax, true, df, null)); cashflows.Add(new Cashflow(Accruals[i], Accruals[i + 1], Accruals[i + 1], prepayment[i], Currency, CashflowType.Prepayment, true, df, null)); cashflows.Add(new Cashflow(Accruals[i], Accruals[i + 1], Accruals[i + 1], defaultPrincipal[i], Currency, CashflowType.PrincipalLossOnDefault, true, df, null)); } return(cashflows.ToArray()); }
private Cashflow[] GetBondCashflows(IMarketCondition market, bool netted = true, bool calcAi = false) { var accruals = Accruals.ToArray(); var schedulePaymentDates = calcAi ? Accruals.Skip(1).ToArray() : PaymentSchedule.ToArray(); if (schedulePaymentDates.Length == 0) { throw new PricingLibraryException("Number of payments is 0"); } List <CfCalculationDetail[]> cfCalcDetails; var cashflows = new List <Cashflow>(); var prevCfDate = accruals[0]; var coupons = Coupon.GetCoupon( Accruals, market.FixingCurve.HasValue ? market.FixingCurve.Value : null, market.HistoricalIndexRates.HasValue ? market.HistoricalIndexRates.Value : null, out cfCalcDetails, IssueRate, _compensationRate); var refStartDates = new List <Date>(); var refEndDates = new List <Date>(); for (var i = 0; i < coupons.Length; ++i) { var refStartDate = accruals[i]; var refEndDate = accruals[i + 1]; if (i == 0 && !_isRegualDate[i]) { if (PaymentFreq != Frequency.None) { if (Stub == Stub.LongStart || Stub == Stub.ShortStart) { refStartDate = PaymentFreq.GetTerm().Prev(refEndDate); } else { refEndDate = PaymentFreq.GetTerm().Next(refStartDate); } } } refStartDates.Add(refStartDate); refEndDates.Add(refEndDate); } IAmortization amortization; if (AmortizationType == AmortizationType.EqualPrincipal || AmortizationType == AmortizationType.EqualPrincipalAndInterest) { //for these two types, amortization is calculated in cash flow calculation var tArr = coupons.Select((x, i) => AccrualDayCount.CalcDayCountFraction(accruals[i], accruals[i + 1], refStartDates[i], refEndDates[i])).ToArray(); double[] begPrincipal; double[] interest; double[] principalPay; double[] prepayment; double[] defaultPrincipal; _mortgageCalculator.GetPaymentDetails(tArr, Notional, coupons, PaymentFreq, AmortizationType, out begPrincipal, out interest, out principalPay, out prepayment, out defaultPrincipal, 0); amortization = new Amortization(ToAmortizationSchedule(schedulePaymentDates, principalPay.Select((x, i) => Tuple.Create(i + 1, x)).ToDictionary(x => x.Item1, x => x.Item2)), false); } else { amortization = Amoritzation.Adjust(new Schedule(schedulePaymentDates), Calendar, calcAi ? AccrualBizDayRule : PaymentBizDayRule, PaymentFreq); amortization = amortization.ResetAmortization(market.ValuationDate); } for (var i = 0; i < coupons.Length; ++i) { var amortizationRedemption = 0.0; var remainPrincipal = 100.0; var couponPay = 0.0; var prevDate = prevCfDate; if (amortization.AmortizationSchedule != null && amortization.AmortizationSchedule.Any(x => x.Key > prevCfDate && x.Key < schedulePaymentDates[i])) { var intermediatePrePayDate = amortization.AmortizationSchedule.Where(x => x.Key > prevCfDate && x.Key < schedulePaymentDates[i]).Select(x => x.Key).OrderBy(x => x).ToArray(); CfCalculationDetail[] tmpCfCalculationDetails; foreach (var date in intermediatePrePayDate) { amortizationRedemption = amortization.GetRemainingPrincipal(prevDate); remainPrincipal = Notional * amortizationRedemption; var periodCoupon = Coupon.GetCoupon(prevDate, date, market.FixingCurve.Value, market.HistoricalIndexRates, out tmpCfCalculationDetails, _compensationRate[i]); couponPay = remainPrincipal * periodCoupon * AccrualDayCount.CalcDayCountFraction(prevDate, date, refStartDates[i], refEndDates[i]); cashflows.Add(new Cashflow(prevDate, date, date, couponPay, Currency, CashflowType.Coupon, tmpCfCalculationDetails.Aggregate(true, (current, v) => current & v.IsFixed), market.GetDf(date), cfCalcDetails[i], refStartDates[i], refEndDates[i], remainPrincipal, periodCoupon)); prevDate = date; } coupons[i] = Coupon.GetCoupon(prevDate, accruals[i + 1], market.FixingCurve.Value, market.HistoricalIndexRates, out tmpCfCalculationDetails, _compensationRate[i]); cfCalcDetails[i] = tmpCfCalculationDetails; } amortizationRedemption = amortization.GetRemainingPrincipal(prevDate); remainPrincipal = Notional * amortizationRedemption; couponPay = remainPrincipal * coupons[i] * AccrualDayCount.CalcDayCountFraction(prevDate, accruals[i + 1], refStartDates[i], refEndDates[i]); if (i == coupons.Length - 1) { amortizationRedemption = amortization.GetRemainingPrincipal(prevCfDate); remainPrincipal = Notional * amortizationRedemption; //final settlement might be adjusted, coupon [MaturityDate, settlementDate) is added if (SettlmentGap != null) { var settlementDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); if (settlementDate > accruals[i + 1]) { var tmpCoupon = double.IsNaN(_settlementCoupon) ? coupons[i] : _settlementCoupon; var additionalCoupon = remainPrincipal * tmpCoupon * AccrualDayCount.CalcDayCountFraction(UnderlyingMaturityDate, settlementDate, refStartDates[i], refEndDates[i]); couponPay += additionalCoupon; } } couponPay = Redemption.GetRedemptionPayment(couponPay, remainPrincipal) - remainPrincipal; } cashflows.Add(new Cashflow(prevDate, accruals[i + 1], schedulePaymentDates[i], couponPay, Currency, CashflowType.Coupon, cfCalcDetails[i] == null ? true : cfCalcDetails[i].Aggregate(true, (current, v) => current & v.IsFixed), market.GetDf(schedulePaymentDates[i]), cfCalcDetails[i], refStartDates[i], refEndDates[i], remainPrincipal, coupons[i])); prevCfDate = accruals[i + 1]; } cashflows.AddRange(amortization.AmortizationSchedule.Keys.Select(key => new Cashflow(key, key, key, amortization.AmortizationSchedule[key] * Notional, Currency, CashflowType.Principal, true, market.GetDf(key), null))); if (netted) { return(cashflows .GroupBy(cf => cf.PaymentDate) .Select(item => new Cashflow(item.Min(x => x.AccrualStartDate), item.Max(x => x.AccrualEndDate), item.Key, item.Sum(entry => entry.PaymentAmount), Currency, CashflowType.Coupon, item.Aggregate(true, (current, v) => current && v.IsFixed), market.GetDf(item.Key), item.Min(x => x.CalculationDetails), item.Min(x => x.RefStartDate), item.Max(x => x.RefEndDate), item.Max(entry => entry.StartPrincipal), item.Sum(entry => entry.CouponRate))) .OrderBy(cf => cf.PaymentDate) .ToArray()); } else { return(cashflows.ToArray()); } }