private static TermStructure CalcCurveFromMaturity(DateTime Maturity, DateTime StartDate, double CouponRate, NextWorkingDayConvention nextDay, int Freq, Dictionary <DateTime, DateTime> Holidays, bool FixedCashPayments) { List <TermPoint> points = new List <TermPoint>(); DateTime pointDate = Maturity; bool lastPoint = true; while (pointDate > StartDate) { if (lastPoint) { // Only do the last point once, decrement the date next time around lastPoint = false; } else { pointDate = pointDate.AddMonths(-(12 / Freq)); } TermPoint point = new TermPoint(); point.Rate = CouponRate; point.PointDate = pointDate; int month = point.PointDate.Month; point.PayDate = GetWorkingDayDate(1, pointDate.AddDays(-1), Holidays, nextDay); if (!FixedCashPayments) { point.PointDate = point.PayDate; } points.Add(point); } ; TermStructure ts = new TermStructure(); for (int i = points.Count - 1; i >= 0; i--) { ts.Points.Add(points[i]); } return(ts); }
private void BootstrapSwapRates(InputData inputData) { // prev rate starts off as the most recent Future rate Rate prev = inputData.FutureData[inputData.FutureData.Count - 1]; foreach (Rate rate in inputData.SwapData) { double fixedCoupon = rate.SwapRate / 100; DateTime lastDate = rate.TermDate.AddMonths(-12 / inputData.SwapFixedPaymentFrequency); TermStructure ts = Functions.GetTermStructure(lastDate, rate.StartDate, rate.StartDate, fixedCoupon, inputData.SwapFixedDayCountConvention, inputData.NextWorkingDay, inputData.SwapFixedPaymentFrequency, inputData.Holidays, false); ts.PrintStructure(); double fixedCash = Functions.PresentValue(rate.StartDate, 0, ts, inputData, inputData.Holidays, false); ts = Functions.GetTermStructure(rate.TermDate, lastDate, lastDate, fixedCoupon, inputData.SwapFixedDayCountConvention, inputData.NextWorkingDay, inputData.SwapFixedPaymentFrequency, inputData.Holidays, false); ts.PrintStructure(); double accrued = 1 + Functions.AccruedForDaysInPeriod(lastDate, rate.TermDate, ts, fixedCoupon, false); double discountFactor = 0; accrued = accrued > 0 ? discountFactor = (1 - fixedCash) / accrued : 1 - fixedCash; // Adjust rates to curve basis if (inputData.DayCountConvention != inputData.SwapFixedDayCountConvention) { rate.Term = Functions.DaysBetween(rate.StartDate, rate.TermDate, inputData.DayCountConvention); } rate.SpotRate = 100 * Functions.GetRateFromDiscountFactor(discountFactor, rate.Term, Functions.DaysInYear(inputData.DayCountConvention)); inputData.Rates.Add(rate); } }
internal static double AccruedForDaysInPeriod(DateTime AccrueFrom, DateTime AccrueTo, TermStructure ts, double Rate, bool FixedCashPayments) { double ret = 0; TermPoint periodStart = ts.Points[0]; TermPoint periodEnd = null; if (AccrueTo > ts.Points[0].PointDate && AccrueFrom < ts.Points[ts.PointCount - 1].PointDate) { // Find the applicable period foreach (TermPoint point in ts.Points) { if (point.PointDate <= AccrueFrom) { periodStart = point; } else if (point.PointDate >= AccrueTo) { periodEnd = point; break; } } // If the caller has asked for Accrued to a point past the end of the term structure it is not valid // and the PeriodEnd variable will be equal to zero if (periodEnd == null) { throw new Exception(string.Format("AccrueTo data [{0}] is past the end of the curve [{1}].", AccrueTo.ToString("dd MMM yy"), ts.Points[ts.PointCount - 1].PointDate.ToString("dd MMM yy"))); } int days = DaysBetween(AccrueFrom, AccrueTo, ts.DayCount); int periodLength = DaysBetween(periodStart.PointDate, periodEnd.PointDate, ts.DayCount); int freq = (int)Math.Round((double)365 / periodLength, 0); // We need to get the rate of the point to which we are accrueing up to // A rate quoted within the Curve is the portion of rate for that period. So the rate according to the curve for a // semi annual bond paying 5% would be 2.5% if (Rate == 0) { Rate = periodEnd.Amount; } if (FixedCashPayments) { if (periodLength > 366) { // it's longer than a year. // We need to work out the accrued as if a coupon "was" paid each year // This will take account of when a Feb 29 occurrs // Split the accrued to before and after an imaginary paydate being one year before the real pay date DateTime couponPoint2 = periodEnd.PointDate; do { DateTime couponPoint = couponPoint2.AddYears(-1); periodLength = DaysBetween(couponPoint, couponPoint2, ts.DayCount); if (couponPoint < AccrueTo) { if (couponPoint2 < AccrueTo) { // We have done the part up to AccrueTo if (AccrueFrom < couponPoint) { days = DaysBetween(couponPoint, couponPoint2, ts.DayCount); } else { days = DaysBetween(AccrueFrom, couponPoint, ts.DayCount); } } else { // Work out accrued from the CouponPoint to the AccrueTo date if (AccrueFrom < couponPoint) { days = DaysBetween(couponPoint, AccrueTo, ts.DayCount); } else { days = DaysBetween(AccrueFrom, AccrueTo, ts.DayCount); } } ret += (Rate * days / periodLength); } couponPoint2 = couponPoint; } while (couponPoint2 < AccrueFrom); } else { if (periodLength != 0) { ret += (Rate / freq) * (days / periodLength); } } } else { switch (ts.DayCount) { case DayCountConvention.e30_360: case DayCountConvention.e30E_360: case DayCountConvention.e30E1_360: case DayCountConvention.eActual_360: ret = Rate * days / 360; break; case DayCountConvention.eActual_365: case DayCountConvention.eActualNL_365: ret = Rate * days / 365; break; case DayCountConvention.eActual_Actual: // Check for leap years int year1 = (int)DateTime.Parse("1 Jan " + AccrueTo.AddYears(1).Year).Subtract(DateTime.Parse("1 Jan " + AccrueFrom.Year)).TotalDays; if (year1 == 731) { // it goes into two years one of which is a leap year // A bit different for this // This assumes that the period is <= 1 year, ie, cannot span more than 2 adjacent years // Days = startpoint up to the first of next year days = (int)DateTime.Parse("1 Jan " + periodEnd.PointDate.Year).Subtract(periodStart.PointDate).TotalDays; // Days2 = the first of the next year to the end point int days2 = (int)periodEnd.PointDate.Subtract(DateTime.Parse("1 Jan " + periodEnd.PointDate.Year)).TotalDays; // Number of days in each seperate period which falls in each year year1 = (int)DateTime.Parse("1 Jan " + periodEnd.PointDate.Year).Subtract(DateTime.Parse("1 Jan " + periodStart.PointDate.Year)).TotalDays; int year2 = (int)DateTime.Parse("1 Jan " + periodEnd.PointDate.Year + 1).Subtract(DateTime.Parse("1 Jan " + periodEnd.PointDate.Year)).TotalDays; ret = (((Rate / year1) * days)) + (((Rate / year2) * days2)); } else { ret = (Rate * days) / year1; } break; } } } return(ret); }
internal static double PresentValue(DateTime ValueDate, double SpreadOverLibor, TermStructure ts, InputData inputData, Dictionary <DateTime, DateTime> Holidays, bool FixedCashPayments) { double ret = 0; // LastPaymentDate should be the date from which we wish to start accrueing interest. // This is either the lastcoupon payment (for fixed cash payments) or the Startdate if that is later and not Fixed Cash Payments // The curve passed in (Rates) would have either the lastcouponpayment or the startdate as the preceding point DateTime lastPayDate = DateTime.MinValue; DayCountConvention dayC = ts.DayCount; foreach (TermPoint point in ts.Points) { lastPayDate = point.PointDate; if (point.PointDate >= ValueDate) { break; } } for (int p = 1; p < ts.PointCount; p++) { DateTime nextPaymentDate = ts.Points[p].PointDate; DateTime nextPayDate = ts.Points[p].PayDate; int daysToDiscount = DaysBetween(ValueDate, nextPayDate, dayC); if (daysToDiscount > 0) { double fixingRate = ts.Points[p].Rate; double discountFactor = GetDiscountFactor(daysToDiscount, inputData.Rates, dayC); int daysToAccrue = DaysBetween(lastPayDate, nextPaymentDate, dayC); double paymentValue = AccruedForDaysInPeriod(ts.Points[p - 1].PointDate, ts.Points[p].PointDate, ts, fixingRate + SpreadOverLibor, FixedCashPayments); ret += paymentValue * discountFactor; } lastPayDate = nextPayDate; } return(ret); }
internal static TermStructure GetTermStructure(DateTime Maturity, DateTime StartDate, DateTime FirstCouponDate, double CouponRate, DayCountConvention dayCount, NextWorkingDayConvention nextDay, int Freq, Dictionary <DateTime, DateTime> Holidays, bool FixedCashPayments) { TermStructure ts = CalcCurveFromMaturity(Maturity, StartDate, CouponRate, nextDay, Freq, Holidays, false); ts.DayCount = dayCount; // Calc daycount depending on accrual rule int yearDays = DaysInYear(dayCount); int point; TermPoint tp; TermPoint ltp; for (point = 1; point < ts.PointCount; point++) { tp = ts.Points[point]; ltp = ts.Points[point - 1]; tp.Term = DaysBetween(ltp.PointDate, tp.PointDate, dayCount); if (FixedCashPayments) { int lastYear = (int)tp.PointDate.Subtract(tp.PointDate.AddYears(-1)).TotalDays; if (tp.Term <= yearDays || lastYear == -366) { tp.EffectiveRate = tp.Rate / Freq * yearDays / tp.Term; } else { tp.EffectiveRate = tp.Rate; } } else { tp.EffectiveRate = tp.Rate; } } point = 0; while (point > -1 && ts.PointCount > 1) { tp = ts.Points[point]; if (tp.PointDate < StartDate) { tp.PointDate = StartDate; tp = ts.Points[point + 1]; tp.Term = DaysBetween(StartDate, tp.PointDate, dayCount); } else if (tp.PointDate > StartDate && tp.PointDate < FirstCouponDate && point != 0) { ts.Points.Remove(tp); if (point < ts.PointCount && point > 0) { tp = ts.Points[point]; ltp = ts.Points[point - 1]; tp.Term = DaysBetween(ltp.PointDate, tp.PointDate, dayCount); } point--; } else if (tp.PointDate >= FirstCouponDate) { point = -2; // get out } point++; } int totalTerm = 0; for (point = 1; point < ts.PointCount; point++) { tp = ts.Points[point]; ltp = ts.Points[point - 1]; // BUT for actual / actual and in a leap year some more calculations need to be done. // e.g. Period 2 jan 96 to 1 jan 97 = (365/366 + 1/365) NOT including the last day but including the first day if (dayCount == DayCountConvention.eActual_Actual && FixedCashPayments == false && ltp.PointDate.Year != tp.PointDate.Year) { tp.YearFraction = ltp.PointDate.Subtract(DateTime.Parse("1 Jan " + ltp.PointDate.AddYears(1).Year)).TotalDays / DateTime.Parse("1 Jan " + ltp.PointDate.AddYears(1).Year).Subtract(DateTime.Parse("1 Jan " + ltp.PointDate.Year)).TotalDays + tp.PointDate.Subtract(DateTime.Parse("1 Jan " + tp.PointDate.Year)).TotalDays / DateTime.Parse("1 Jan " + tp.PointDate.AddYears(1).Year).Subtract(DateTime.Parse("1 Jan " + tp.PointDate.Year)).TotalDays; } else { if (tp.Rate > 0 && FixedCashPayments) { tp.YearFraction = tp.EffectiveRate * tp.Term / yearDays / tp.Rate; } else { tp.YearFraction = (double)tp.Term / (double)yearDays; } } tp.Amount = tp.Rate * tp.YearFraction; // Reset daycounts to be cummulative totalTerm += tp.Term; tp.Term = totalTerm; } return(ts); }