« Project Vector? You mean like “attack” vector? | Main | Discount/Zero Curve Construction in F# – Part 1 (The End) »

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"

Here there are two examples of pattern matching: firstly to determine when to end the recursion (when there are no more days by which we need to roll), and then to roll appropriately depending on the rule (just two rules are shown here for brevity). rollBy in turn makes use of the more basic functions dayAfter and roll:
let dayAfter (date:Date) = date.AddDays(1.0)
let rec roll rule calendar date =
    if isBusinessDay date calendar then
        match rule with
        | RollRule.Actual -> date
        | RollRule.Following -> dayAfter date |> roll rule calendar
        | _ -> failwith "Invalid RollRule"

Where dayAfter is not much more than an alias for readability, and roll is a function that rolls any day that happens to be a holiday or weekend. The real decision about the “goodness” of a business day is handled by the isBusinessDay function, a simple lookup into the calendar:
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

Now we have our quotes, and some routines to handle rolling to good business days, we can begin to establish the benchmark dates that will form the discount curve. First, we can pull out the quotes up to spot, and roll each quote by an appropriate number of days:
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)
This is our first look at list functions. Our quotes form a list, and List.choose is a function that allows us to iterate through the list, making a decision on each item as to whether or not we want to include it (the Some option), or exclude it (the None option) – exactly what we need to pull out the overnight and tomnext quotes, and roll the curve date appropriately in order to establish each discount point’s date:

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)

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)
First we find the first and last futures quotes, and compute dates of the start and end of the futures schedule. Then we create the extra point covering the final contract. I’ve omitted findNthWeekDay, but it’s not complicated and fairly self-explanatory. Now for the real work:
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
This pipeline operates on a sequence, which is the F# equivalent of IEnumerable<T>. Here we first append our extra contract, then use the Seq.pairwise function to iterate through the sequence two-by-two. This pairing allows us to take the value of the quote from the first of each pair, and combine it with the contract of the quote from the second of the pair. The effect is to “shift” the quote values down the schedule to their subsequent contract. At the same time, we find the correct contract date (and roll it if necessary), and convert the quote to a rate. At the end of this dance we have the following futures points ready for bootstrapping (in part 3).

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (2)

Great article, I like it a lot. After 10 years of imperative languages i'm really enjoying functional languages. One question however, how did you define 'RollRule.Actual' and 'RollRule.Following'?




January 18, 2010 | Unregistered CommenterKoen


I have RollRule as a simple discriminated union (essentially an enum for this purpose):

type RollRule =
| Actual = 0
| Following = 1

February 12, 2010 | Registered CommenterlessCode

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
All HTML will be escaped. Hyperlinks will be created for URLs automatically.