## Discount/Zero Curve Construction in F# – Part 2 (Rolling Dates)

In part 1, we used the rollBy function to find the spot date from the curve date:

let spotDate = rollBy 2 RollRule.Following USD curveDate

Unsurprisingly, this rolls by two business days; according to the *following* roll rule; accounting for business holidays in the United States; the curve date. I guess there are several ways to implement this function – recursively is one way:

let rec rollBy n rule calendar (date:Date) = match n with | 0 -> date | x -> match rule with | RollRule.Actual -> date.AddDays(float x) | RollRule.Following -> dayAfter date |> roll rule calendar |> rollBy (x - 1) rule calendar | _ -> failwith "Invalid RollRule"

let dayAfter (date:Date) = date.AddDays(1.0) let rec roll rule calendar date = if isBusinessDay date calendar then date else match rule with | RollRule.Actual -> date | RollRule.Following -> dayAfter date |> roll rule calendar | _ -> failwith "Invalid RollRule"

let isBusinessDay (date:Date) calendar = not (calendar.weekendDays.Contains date.DayOfWeek || calendar.holidays.Contains date)

And to save space I’ll use a US holiday calendar for only 2009:

let USD = { weekendDays = Set [ System.DayOfWeek.Saturday; System.DayOfWeek.Sunday ]; holidays = Set [ date "2009-01-01"; date "2009-01-19"; date "2009-02-16"; date "2009-05-25"; date "2009-07-03"; date "2009-09-07"; date "2009-10-12"; date "2009-11-11"; date "2009-11-26"; date "2009-12-25" ] }

### Discount Dates

let spotPoints = quotes |> List.choose (fun (t, q) -> match t with | Overnight _ -> Some (rollBy 1 RollRule.Following USD curveDate, q) | TomorrowNext _ -> Some (rollBy 2 RollRule.Following USD curveDate, q) | _ -> None) |> List.sortBy (fun (d, _) -> d)

Cash quotes get similar treatment, except that now, instead of rolling from the curve date, we’re offsetting from the spot date (and rolling if necessary):

let cashPoints = quotes |> List.choose (fun (t, q) -> match t with | Cash c -> Some (offset c spotDate |> roll RollRule.Following USD, q) | _ -> None) |> List.sortBy (fun (d, _) -> d)

Where offset simply adds the relevant number of days, months and/or years to a date:

let offset tenor (date:Date) = date.AddDays(float tenor.days) .AddMonths(tenor.months) .AddYears(tenor.years)

Swaps – same story:

let swapPoints = quotes |> List.choose (fun (t, q) -> match t with | Swap s -> Some (offset s spotDate |> roll RollRule.Following USD, q) | _ -> None) |> List.sortBy (fun (d, _) -> d)

Futures – not nearly so simple. The June 2009 Eurodollar contract covers the three month period starting on the 3rd Wednesday in June, and ending on the 3rd Wednesday in September, so the quotes actually correspond to discount dates represented by the

*next*futures contract in the schedule. This means we will interpolate the discount point for June (the start of the futures schedule), and add an extra point after the last quote (the end of the futures schedule):

let (sc, _) = List.hd futuresQuotes let (ec, _) = futuresQuotes.[futuresQuotes.Length - 1] let futuresStartDate = findNthWeekDay 3 System.DayOfWeek.Wednesday sc |> roll RollRule.ModifiedFollowing USD let futuresEndDate = (new Date(ec.Year, ec.Month, 1)).AddMonths(3) // "invent" an additional contract to capture the end of the futures schedule let endContract = (futuresEndDate, 0.0)

let futuresPoints = Seq.append futuresQuotes [endContract] |> Seq.pairwise |> Seq.map (fun ((_, q1), (c2, _)) -> (findNthWeekDay 3 System.DayOfWeek.Wednesday c2 |> roll RollRule.ModifiedFollowing USD, (100.0 - q1) / 100.0)) |> Seq.to_list