## 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 })))

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))

let newton f df (guess:double) = guess - f guess / df guess

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

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)

let zeroCouponRates = discs |> Seq.map (fun (d, f) -> (d, 100.0 * -log(f) * 365.0 / double (d - curveDate).Days))