Message Boards Message Boards


Fast curve building with linear solving method

Posted 1 month ago
4 Replies
5 Total Likes


We discuss the mechanics of yield curve bootstrapping and demonstrate how the use of linear solver speeds up the process. Once the market data for curve construction is available, the bootstrapping process is quite quick and the entire yield curve is obtained in a single step. This optimises the bootstrapping procedures and leads to an elegant and transparent solution. The auxiliary output - the so-called yield curve derivatives such as zero and forward rates can be generated on the fly.

enter image description here

The yield curve methodology

Yield curves are one of the most fundamental concepts in finance. Curves are basic building blocks for many financial instruments and they are key determinants of value creation due to the discounting effect. Yield curves - when built with derivative instruments - are essential for forward rates calculation. As such, they are critical component of the entire market for interest rate derivatives.

Yield curves are created through bootstrapping - a technique that converts market observable rates into 'zero-coupon' instruments. To be consistent and correct, the yield curve has to satisfy the following condition:

Sum[ c[[i]] g[[i]] DF[[i], {i,1, n-1}] + (1+c[[n]] g[[n]]) DF[[n]] ==1

where c is the fixed market rate, g is the year-fraction and DF stands for discount factor that we are trying to bootstrap

When we choose the 'fixed leg' method, the above expression guarantees that all instruments on the yield curve will price to par. This ensures that discount factors are correctly calculated.

The curve equation expression above ensures the consistency for a single curve pillar - i.e. the maturity point on the curve When $m$ such instruments are lined up together, we obtain a matrix of curve equations that we can solve with linear solver:

Curve bootstrapping - example

We demonstrate the curve building principles with the following simple example:

  • Randomize market data for the curve construction
  • Visualise the data as the curve input

    c=0.0055+RandomReal[{0.005,0.009}] Sqrt[act];
    ListLinePlot[td,PlotStyle->{Blue, Thickness[0.008]},GridLines->{Automatic,Automatic},PlotLabel->Style["Yield curve market quotes",Purple,15]]

enter image description here

Using the market data above, we construct matrix of cash flows and use LinearSolve method to obtain the required discount factors.

ListLinePlot[td2,PlotLabel->Style["Discount factors",Blue,15],GridLines->{Automatic, Automatic},PlotStyle->{Red,Thickness[0.009]}]

enter image description here

The obtained discount factors are unique, monotonically decreasing and in right order. Having discount factors computed allows us to obtain all kind of measures that depend on discount factors:

  1. Zero-coupon rates These are the unique rates that pay out single cash flow at maturity and are essentially 'inverted' DF $zero rate = Log[1/DF]/T$

    ListLinePlot[td3,PlotLabel->Style["Zero rates",Blue,15],GridLines->{Automatic, Automatic},PlotStyle->{Magenta,Thickness[0.009]}]

enter image description here

  1. Forward rates Forward rates are 'special' as they are rates observed today but starting in the future. They are market expectations of future short-term rates and therefore important indicators of future level of interest rates in the economy. We calculate a series of 3-monthly forward rates from the discount factors as follows:

    forward_rate[t1,t2] =( DF[t1]/DF[t2]-1)/g[t1,t2]

where $t1$ and $t2$ are times in the future, $DF$ are discount factors at time point $t1$ and $t2$ and $g[t1,t2]$ is the year fraction between
time point $t1$ and $t2$, with $t2 >t1$

In order to compute forward rates, we need to introduce a simple function that computes the business day. That can be easily done using the built-in functional components:

OpenDay[start_,incr_,itype_]:=NestWhile[DatePlus[#,1]&,DatePlus[start,{incr,itype} ],Not[BusinessDayQ[#]]&]

and we can test it


and get the correct day - Monday, 23rd July 18

To compute forward rates, we first generate forward dates, calculate the time interval, interpolate DF from the DF object and then use the forward rate formula to get the rate:

fwrates=Table[(intdf[i]/intdf[i+kd]-1)/kd,{i,kd,60 kd, kd}]//Quiet;
td4=TemporalData[fwrates,{Range[kd,60 kd, kd]}];
ListLinePlot[td4,PlotTheme->"Web",PlotLabel->Style["Smooth Forward rates",Blue,15]]

enter image description here

We have obtained smooth forward rates from the generated discount factors - this is an evidence that the discount factors were properly calculated and the choice of interpolation was appropriate.

When the yield curve is upward sloping, then the mathematics of forward rates will cause forward rates being higher than the zero rates. We can see this is the case:

ListLinePlot[{td3,td4},PlotTheme->"Web",PlotLabel->Style["Zero and Forward rates",Blue,15], PlotLegends->{"Zero", "Forward"}]  

enter image description here

Real-case example

Having explained the yield curve bootstrapping methodology, we can now move to the real-case scenario where we will the actual market data to construct the Australian Dollar (AUD) curve. We use deposit rates in the short-end and the swap rates in the mid-to-long end of the curve with maturity up to 30 years. The swap will pay out semi-annually.

pildates={OpenDay[Today,1, "Month"],OpenDay[Today,2, "Month"],  Table[OpenDay[Today, 3 i, "Month"],{i,4}],OpenDay[Today,18, "Month"], Table[OpenDay[Today, i, "Year"],{i,2,10 }], OpenDay[Today,12, "Year"],OpenDay[Today,15, "Year"],OpenDay[Today,20, "Year"],OpenDay[Today,25, "Year"],OpenDay[Today,30, "Year"]}//Flatten;
pilyrs=DateDifference[pildates   ,"Year",DayCountConvention->"Actual365"][[All,1]] ;
intQ=Interpolation[tdQ ,Method->"Spline"];
cfdates={Today,OpenDay[Today,1, "Month"],OpenDay[Today,2, "Month"],  Table[OpenDay[Today, 3 i, "Month"],{i,4}],Table[OpenDay[Today, (12/pmfreq ) i, "Month"],{i,3,30 pmfreq }]}//Flatten;
cfyrs=Drop[DateDifference[cfdates   ,"Year",DayCountConvention->"Actual365"][[All,1]],1];
cfyD=Length[cfyrs ]; 
intrates=intQ [cfyrs ];
ListLinePlot[intrates,PlotLabel->Style["AUD yield curve term structure",Blue,{15,Bold}],PlotStyle->{Magenta, Thick}]

enter image description here

We first split the market data into (i) deposits and (ii) swap segments:

depoyrs={Take[ cfyrs ,3],Differences[Take[cfyrs,{3,6}]]}//Flatten;
swapData=Inner[{#1,#2}&,Drop[cfyrs,6] ,Drop[intrates ,6],List];
swapyrs={0,cfyrs[[4]],cfyrs[[6]] ,Take[cfyrs,{7,cfyD}]}//Flatten;
swyfrac=Differences[swapyrs ];

and built the matrix of linear equations that we solve for the discount factors:

zTab1=Table[If[i==j,1+intrates[[j]] If[i<=2,depoyrs [[i]],fraAv ],0],{j,6},{i,cfyD}];
zTab2=Table[If[MemberQ[swapyrs,cfyrs[[i]]],If[cfyrs[[i]]<swapData[[j,1]],swapData[[j,2]] swyAv ,If[cfyrs[[i]]==swapData[[j,1]],1+swapData[[j,2]] swyAv  ,0]],0],{j,swapD},{i,1,cfyD}]; 
dfact=LinearSolve[zTab0 ,b1Vec ];
ListLinePlot[tdDf   ,PlotLabel->Style["Generated Discount factors",Blue,15],PlotStyle->{Thickness[0.01],Purple},PlotRange->{Full,{1,0.3}},InterpolationOrder->2]

enter image description here

This is the actual AUD yield curve built from derivative instruments that can be used to value various AUD instruments.

Curve smoothing

Since we use the actual market data, the generated discount factors are not as smooth as in our first example. This is particularly visible in the short-end of the curve. The existence of kinks is due to several factors - different segments of the the curve are being traded by different desks and equilibrium market rates are obtained through different price discovery. If curve kinks and peaks are undesirable, there exist several techniques to smooth them sway and we look at linear filters that can help to accomplish this task. The built-in Mathematica GaussianFilter is particularly useful as it provides required level of control to manipulate 'noisy' data.

For example, applying GaussianFilter with radius $r$ = 3 and standard deviation $sigma$ =2 provide nice and smooth output the the 'kinky' segment of the curve:

tdDF2=TemporalData[Map[iDF,Range[0.25,30,0.25]] ,{Range[0.25,30,0.25]}]; 
ListLinePlot[{Take[orD ,10],Take[flD,10]},PlotLabel->Style["Curve correction with Gaussian filter",Blue,15],PlotLegends->{"Original", "Filtered"},PlotStyle->{Thickness[0.008],Thickness[0.008]},InterpolationOrder->3]

enter image description here

The filtered output produces decent curve with smoother short-end that, if needed, can be used to construct all types of derivatives with appropriate rates. It has to be noted that the filter can be applied both to (i) discount factors and/or (ii) forward rates - depending on the required objective. Generally, the higher the radius, the smoother the output.


The usage of linear solver and its application in curve bootstrapping leads to fast and accurate curve construction with unique discount factors. The function can be easily applied to any environment where the term structure of market data is duly defined. We have also demonstrated the use of filtering technique for curve smoothing with built-in Mathematica filters that provide rexcellent tool for output manipulation if the curve kinks become an issue.

4 Replies

enter image description here - Congratulations! This post is now a Staff Pick as distinguished by a badge on your profile! Thank you, keep it coming!

Thank you. Will do my best! IH


Thanks again for an interesting post. You may be interested that Will Shaw did something similar in evaluating zero coupon and yield curves in his book "Modeling Financial Derivatives with Mathematica". Is it possible that you could also supply the notebook behind this post

Thank you Michael

Thank you, Michael. I was not aware of the fact Will presented the same concept in his book..... I was working on this matrix idea recently and thought it might be of interest to the entire community. Mathematica is well suited for this task.

I will have to build the notebook separately as I typed the idea straight into the editor. Best Igor

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
or Discard

Group Abstract Group Abstract