private void GenerateSchedule(DateTime asOf, DateTime startDate, DateTime endDate, DayCount dayCount, DayRule dayRule, CurveTenor tenor, StubPlacement stub = StubPlacement.NullStub) { // This only works for short stubs atm, although NullStub will generate a long stub DateTime AdjStart = DateHandling.AdjustDate(startDate, dayRule); DateTime AdjEnd = DateHandling.AdjustDate(endDate, dayRule); string tenorString = EnumToStr.CurveTenor(tenor); string TenorLetter = DateHandling.GetTenorLetterFromTenor(tenorString); double TenorNumber = DateHandling.GetTenorNumberFromTenor(tenorString); int periods = CalculatePeriods(startDate, endDate, tenor); // Create estimate of how long the schedule should be //double YearsUpper = DateHandling.Cvg(AdjStart, AdjEnd, dayCount); //double YearLower = DateHandling.Cvg(AsOf, AdjStart, dayCount); // Will be sorted at end (when coverages are also calculated) UnAdjStartDates.Add(StartDate); UnAdjEndDates.Add(EndDate); AdjStartDates.Add(AdjStart); AdjEndDates.Add(AdjEnd); //if (StrToEnum.ConvertTenorLetter(TenorLetter) == Tenor.M) //{ // periods = periodsInMonths / (int)TenorNumber; // //WholePeriods = periodsInMonths / (int) TenorNumber; // //double tempPeriods = YearsUpper * 12 / TenorNumber; // //WholePeriods = (int)Math.Truncate(tempPeriods); //} //else if (StrToEnum.ConvertTenorLetter(TenorLetter) == Tenor.Y) //{ // periods = periodsInMonths / (12 * (int)TenorNumber); // //WholePeriods = (int) Math.Truncate(YearsUpper); //} //else //{ // throw new ArgumentException("Can only roll out swap calender for month and year tenors"); //} if (stub == StubPlacement.Beginning) { periods += 1 * 12 / (int)Math.Round(TenorNumber); for (int i = 1; i < periods; i++) { UnAdjEndDates.Add(DateHandling.AddTenorAdjust(UnAdjEndDates[i - 1], "-" + tenorString)); AdjEndDates.Add(DateHandling.AdjustDate(UnAdjEndDates[i], DayRule)); UnAdjStartDates.Add(UnAdjEndDates[i]); AdjStartDates.Add(DateHandling.AdjustDate(UnAdjStartDates[i], DayRule)); } } else if (stub == StubPlacement.End) { periods += 1 * 12 / (int)Math.Round(TenorNumber); for (int i = 1; i < periods; i++) { UnAdjStartDates.Add(DateHandling.AddTenorAdjust(UnAdjStartDates[i - 1], tenorString)); AdjStartDates.Add(DateHandling.AdjustDate(UnAdjStartDates[i], DayRule)); UnAdjEndDates.Add(UnAdjStartDates[i]); AdjEndDates.Add(DateHandling.AdjustDate(UnAdjEndDates[i], DayRule)); } } else if (stub == StubPlacement.NullStub) { for (int i = 1; i < periods; i++) { UnAdjEndDates.Add(DateHandling.AddTenorAdjust(UnAdjEndDates[i - 1], "-" + tenorString)); AdjEndDates.Add(DateHandling.AdjustDate(UnAdjEndDates[i], DayRule)); UnAdjStartDates.Add(UnAdjEndDates[i]); AdjStartDates.Add(DateHandling.AdjustDate(UnAdjStartDates[i], DayRule)); } } // Sort dates according to date UnAdjStartDates.Sort(new Comparison <DateTime>((x, y) => x.CompareTo(y))); UnAdjEndDates.Sort(new Comparison <DateTime>((x, y) => x.CompareTo(y))); AdjStartDates.Sort(new Comparison <DateTime>((x, y) => x.CompareTo(y))); AdjEndDates.Sort(new Comparison <DateTime>((x, y) => x.CompareTo(y))); for (int i = 0; i < AdjStartDates.Count; i++) { Coverages.Add(DateHandling.Cvg(AdjStartDates[i], AdjEndDates[i], DayCount)); } }
// Ideally, this should be a derived class on SwapSchedule, since an // OIS schedule (in this context) is either a short period, or 1Y schedule with // a stub in the end. For our purpose, we just define a derived the class // that only works for our purpose (i.e. it's not general.) public OisSchedule(DateTime asOf, string startTenor, string endTenor, string settlementLag, DayCount dayCount, DayRule dayRule) : base(asOf, startTenor, endTenor, dayCount, dayRule) { Tenor startTenorEnum = GetLetterFromTenor(startTenor); Tenor endTenorEnum = GetLetterFromTenor(endTenor); UnAdjStartDates.Add(StartDate); AdjStartDates.Add(StartDate); // Just to make sure we compare with both "1Y" and "12M" (because i'm lazy in correcting for DayCount) if (CompareTenors(endTenor, "1Y") == false && CompareTenors(endTenor, "12M") == false) { // Simple OIS swap double cvg = DateHandling.Cvg(StartDate, EndDate, dayCount); UnAdjEndDates.Add(EndDate); AdjEndDates.Add(DateHandling.AdjustDate(EndDate)); Coverages.Add(cvg); return; } else { int months = 0; // 1Y periods + stub int periods, years; if (endTenorEnum == Tenor.Y) { periods = GetNumberFromTenor(endTenor); years = periods; } else if (endTenorEnum == Tenor.M) { years = (int)Math.Truncate(GetNumberFromTenor(endTenor) / 12.0); if (GetNumberFromTenor(endTenor) % 12 == 0) { months = 0; periods = years; } else { months = GetNumberFromTenor(endTenor) - 12 * years; periods = years + 1; } } else { throw new InvalidOperationException("OIS Schedule only works for Y,M endTenors"); } UnAdjEndDates.Add(DateHandling.AddTenorAdjust(StartDate, "1Y")); AdjEndDates.Add(DateHandling.AdjustDate(UnAdjEndDates[0], DayRule.N)); Coverages.Add(DateHandling.Cvg(AdjStartDates[0], AdjEndDates[0], DayCount)); // Generate Schedule // We start from 1 since first days are filled out // We only end here if we have more than 1 period for (int j = 1; j <= periods; j++) { if (periods > years && periods == j + 1) // In case we have tenor like "18M" and have to create a stub periods { string excessTenor = months.ToString() + "M"; UnAdjStartDates.Add(DateHandling.AddTenorAdjust(UnAdjStartDates[j - 1], "1Y", dayRule)); AdjStartDates.Add(DateHandling.AdjustDate(UnAdjStartDates[j], dayRule)); UnAdjEndDates.Add(DateHandling.AddTenorAdjust(UnAdjEndDates[j - 1], excessTenor, dayRule)); AdjEndDates.Add(DateHandling.AdjustDate(UnAdjEndDates[j], dayRule)); Coverages.Add(DateHandling.Cvg(AdjStartDates[j], AdjEndDates[j], DayCount)); } else { if (j < periods) { UnAdjStartDates.Add(DateHandling.AddTenorAdjust(UnAdjStartDates[j - 1], "1Y", dayRule)); AdjStartDates.Add(DateHandling.AdjustDate(UnAdjStartDates[j], dayRule)); UnAdjEndDates.Add(DateHandling.AddTenorAdjust(UnAdjEndDates[j - 1], "1Y", dayRule)); AdjEndDates.Add(DateHandling.AdjustDate(UnAdjEndDates[j], dayRule)); Coverages.Add(DateHandling.Cvg(AdjStartDates[j], AdjEndDates[j], DayCount)); } } } } }