Discount/Zero Curve Construction in F# – Part 4 (Core Math)

All that’s left to cover in this simplistic tour of discount curve construction is to fill in the implementations of the core mathematics underpinning the bootstrapping process.

computeDf simply calculates the next discount factor based on a previously computed one:

let computeDf fromDf toQuote =
    let dpDate, dpFactor = fromDf
    let qDate, qValue = toQuote
    (qDate, dpFactor * (1.0 / 
                        (1.0 + qValue * dayCountFraction { startDate = dpDate; 
                                                           endDate = qDate })))
 
Where for dayCountFraction we’ll assume an Actual/360 day count convention, but this could be generalized to pass the day counting method as a function parameter to computeDf:
 
  let dayCountFraction period = double (period.endDate - period.startDate).Days / 360.0


findDf looks up a discount factor on the curve, for a given date, interpolating if necessary. Again, here tail recursion and pattern matching make this relatively clean:

  let rec findDf interpolate sampleDate =
    function
      // exact match
      (dpDate:Date, dpFactor:double) :: tail 
        when dpDate = sampleDate
        -> dpFactor
            
      // falls between two points - interpolate    
    | (highDate:Date, highFactor:double) :: (lowDate:Date, lowFactor:double) :: tail 
        when lowDate < sampleDate && sampleDate < highDate
        -> interpolate sampleDate (highDate, highFactor) (lowDate, lowFactor)
      
      // recurse      
    | head :: tail
        -> findDf interpolate sampleDate tail
      
      // falls outside the curve
    | [] 
        -> failwith "Outside the bounds of the discount curve"

logarithmic does logarithmic interpolation for a date that falls between two points on the discount curve. This function is passed by value as the interpolate parameter to findDf above:

  let logarithmic (sampleDate:Date) highDp lowDp = 
    let (lowDate:Date), lowFactor = lowDp
    let (highDate:Date), highFactor = highDp
    lowFactor * ((highFactor / lowFactor) ** 
                 (double (sampleDate - lowDate).Days / double (highDate - lowDate).Days))

Newton’s Method is quite straightforward in F#:
 
let newton f df (guess:double) = guess - f guess / df guess

To recursively solve using Newton’s Method to a given accuracy:
 
  let rec solveNewton f df accuracy guess =
    let root = (newton f df guess)
    if abs(root - guess) < accuracy then root else solveNewton f df accuracy root

And all that remains are the functions that feed Newton; the price of the market swap and its first derivative – note that this is certainly not the most efficient way to do this, because to compute the derivative we recalculate the price in order to approximate the derivative with a finite-difference method:
 
  let deriv f x =
    let dx = (x + max (1e-6 * x) 1e-12)
    let fv = f x
    let dfv = f dx
    if (dx <= x) then
        (dfv - fv) / 1e-12
    else
        (dfv - fv) / (dx - x)

  let computeSwapDf dayCount spotDate swapQuote discountCurve swapSchedule (guessDf:double) =
    let qDate, qQuote = swapQuote
    let guessDiscountCurve = (qDate, guessDf) :: discountCurve 
    let spotDf = findDf logarithmic spotDate discountCurve
    let swapDf = findPeriodDf { startDate = spotDate; endDate = qDate } guessDiscountCurve
    let swapVal =
        let rec _computeSwapDf a spotDate qQuote guessDiscountCurve =
            function
              swapPeriod :: tail ->
                let couponDf = findPeriodDf { startDate = spotDate; endDate = swapPeriod.endDate } guessDiscountCurve
                _computeSwapDf (couponDf * (dayCount swapPeriod) * qQuote + a) spotDate qQuote guessDiscountCurve tail

            | [] -> a
        _computeSwapDf -1.0 spotDate qQuote guessDiscountCurve swapSchedule
    spotDf * (swapVal + swapDf)

And finally, the zero coupon rates can be found with something like the following direct mapping, on the basis that there are 365 days in a year:
 
  let zeroCouponRates = discs
                      |> Seq.map (fun (d, f) 
                                    -> (d, 100.0 * -log(f) * 365.0 / double (d - curveDate).Days))


All in all, I think that a functional language like F# provides a much simpler means to code these types of calculation over a typical C or C++ implementation (notwithstanding performance considerations, which I’ve yet to study in any depth, but I have a hunch that with the advent of cloud computing and massively multicore hardware, that for this kind of work it’s going to become less and less about “straight-line” speed in clock cycles or operations, and more and more about the simplicity with which we can reason about such code in order to find inherent functional parallelism).
I’m also wondering about the benefits of functional programming when it comes to pricing certain derivative trades with payoff formulae that might be maintained by an end-user-trader or quantitative analyst without requiring anything we’d currently regard as “software engineering”. This question touches on the use of functional languages as Domain-Specific Languages (DSLs), and may be the topic of a future series of posts as time permits…
Previous
Previous

Windows Mobile got me fired (or at least it could have done)

Next
Next

Discount/Zero Curve Construction in F# – Part 3 (Bootstrapping)