Project · 2025—2026

Effective Risk-Free Rate

The risk-free rate is not one number. It depends on the maturity, the market, and the convention the math expects. This engine settles that. Pick a date, choose the United States or Canada, and get the effective rate in the exact form each application needs, from two live curves side by side.

Role Sole designer and builder Stack Python, Vanilla JS, GitHub Actions Data New York Fed, FRED, Bank of Canada

01 Overview

One rate, quoted the way each job needs it

Ask three desks for the risk-free rate and you get three answers. The option desk wants a continuous rate on an ACT/365 basis for Black-Scholes. The bond desk wants a semiannual bond-equivalent yield. The money-market desk wants a simple rate, and the day-count is ACT/360 in the United States but ACT/365 in Canada. Same idea, different clothes.

I wanted a tool that holds the idea still and changes only the clothes. The whole engine rests on one primitive, the discount factor for a given horizon. Every quoted rate is derived from that single number, so the option rate, the bond rate, the money-market rate and the implied forward all stay consistent with each other by construction.

It covers two markets. The United States, with Treasury yields from FRED and SOFR taken straight from the New York Fed that publishes it, and Canada, off the Bank of Canada. Each market shows a government zero curve and a curve anchored to the overnight rate, so you can read the risk-free rate and the funding rate at once.

The right rate is the one quoted in the convention your model already assumes. Everything else is a rounding error waiting to happen.

02 What it computes

From the discount factor outward

One primitive, four conventions, two curves, two markets.

Every convention

Continuous ACT/365 for option Greeks, semiannual bond-equivalent for valuation, and simple money-market rates on the correct ACT/360 or ACT/365 basis. All from one discount factor.

Two curves per market

A government zero curve bootstrapped from par yields, and a curve anchored to the realised overnight rate. Read the risk-free rate and the funding rate against each other.

Any maturity

Type a date or a time to maturity and the curve is interpolated on the spot, log-linear in the discount factor, which holds the forward rate constant between knots. Quick chips land on the published windows, 1M through 30Y. The chart axis is a separate choice: linear through the first six months, then logarithmic, so a three-month option and a thirty-year bond are both legible on one axis.

Implied forwards

Pick two future dates and get the forward rate between them, in both continuous and simple form, straight from the ratio of discount factors.

Where the data ends

The overnight curve is published data out to 180 days. Past that it becomes a labelled proxy, because no free feed carries OIS swap quotes. The tool draws the boundary on the chart and flags it in the table.

Built for reading

Choose a market, then work in English or French, light or dark. A methodology tab lists the exact series behind every curve with its own observation date.

03 Under the hood

Curve math in Python, pricing in the browser

A scheduled Python job fetches the inputs and bootstraps the curves. Government par yields go through the standard semiannual par-bond recursion to zero-coupon discount factors. The overnight short end is genuine: on the U.S. side it is the New York Fed's official 30, 90 and 180-day SOFR Averages, and on the Canadian side the CORRA Compounded Index, where the discount factor over a window is the exact realised index ratio, with no day-count assumption at all.

The original version ran a small server that held the FRED key and priced on request. For the public build I turned that inside out. The job writes one static snapshot per market, and the pricing math is ported to the browser, so interpolating the discount factor and converting it into every convention happens on your machine. No server to run, no key exposed, and it loads instantly.

A GitHub Action rebuilds both markets every business day at 09:00 ET, then commits the snapshots so the site redeploys on its own. The FRED key lives only as an encrypted repository secret. The New York Fed and Bank of Canada feeds need no key at all.

Python New York Fed API FRED API Bank of Canada Valet GitHub Actions Vanilla JS Bilingual UI

04 Method

The curve, written out

Two decisions define a curve. What sits at the knots, and what happens between them. The first is a data question and the section above answers it. The second is a modelling choice, and it is the one most tools leave unstated.

The primitive

Everything is a discount factor. The continuous zero rate is not a second object, it is the same number read on a logarithmic scale.

DF(t) = ez(t)t , z(t)= lnDF(t) t
Time is ACT/365 throughout the curve object. The day-count conventions arrive later, at the point of quotation, so the curve itself never carries a convention.

Interpolation: log-linear on discount factors

Between two knots the curve interpolates the logarithm of the discount factor linearly in time. Every value the calculator returns at a maturity that is not a published tenor comes from this one rule.

lnDF(t) = lnDF(ti) + tti ti+1ti [ lnDF(ti+1) lnDF(ti) ]
Equivalently, the instantaneous forward rate is piecewise constant. Differentiating the line above gives a forward that is flat across each interval, which is the same method under its other name.
f(t) = d dt lnDF(t) = lnDF(ti) lnDF(ti+1) ti+1ti , t(ti,ti+1]
Beyond the last knot the rule extends rather than stops: the final interval's forward is held flat, so the curve never bends on evidence it does not have. The same treatment applies before the first knot.

Why this method and not another

Three reasons, in order of how much they matter here.

It is arbitrage-free by construction. As long as the knots have decreasing discount factors, no interpolated point can produce a negative forward rate, because the forward on each interval is a single constant derived from the endpoints. The property is structural rather than something to check afterwards.

It reproduces the knots exactly. Ask for the 180-day rate and the published SOFR Average comes back out unchanged rather than approximated, which is the round-trip the provenance section leans on.

It does not oscillate. A cubic spline through the same knots looks smoother and pays for it with forward rates that wobble between the points, sometimes turning negative near a kink in the input yields. That is a poor trade in a curve whose entire job is to be read at arbitrary maturities. Monotone convex is the genuine step up and it is the right answer when you need smooth, well-behaved forwards for a term-structure model. For quoting a discount factor it buys very little over piecewise constant forwards, and it costs the exact knot reproduction above.

Where the knots come from

Government par yields are bootstrapped on a semiannual grid. Each node is solved from the ones before it, which is the standard par-bond recursion written as a forward substitution.

DF(Tn) = 1c k=1 n1 DF(Tk) 1+c , c= y(Tn) 2 , Tn= n2
A par bond prices to 1, so its coupon stream and principal must discount back to par. Solving that identity for the newest discount factor gives the line above. Par yields between published tenors are interpolated linearly before entering the recursion, and the bootstrap raises rather than clamps if an input yield implies an impossible discount factor.

The overnight short end is not bootstrapped at all, because the administrators publish realised compounding directly. A SOFR Average over a window is a simple money-market rate, and the CORRA Compounded Index is a level whose ratio is already a discount factor with no day-count assumption in it.

DF(t) = 1 1+rd360 DF(t) = I(t0) I(t)
Left, a published SOFR Average r over d days on its ACT/360 basis. Right, the CORRA Compounded Index I, where the realised discount factor is an exact index ratio.

Past the last published overnight window the curve is a labelled proxy, and this is the formula being labelled. A single spread is measured at the anchor and added to the government zero curve, so the shape past that point is the government curve's shape and carries no independent overnight information.

zon(t) = zgov(t) +s , s= zon(ta) zgov(ta) , t>ta
The anchor ta is 180 days, the last published window. The calculator tags every value past it, the chart dashes the line, and the methodology tab prints the measured spread in basis points.

Reading the discount factor

The conventions are quotation, not modelling. One discount factor, four ways of saying it, with d the actual calendar days to maturity.

rcont = lnDFd/365 rann = DF365/d1 rsemi = 2(DF365/2d1) r360 = (1DF1)360d r365 = (1DF1)365d
  • r_cont continuous ACT/365, the rate Black-Scholes expects in e−rT
  • r_semi semiannual bond-equivalent, for bond valuation
  • r_360 simple ACT/360, US money market and swaps; r_365 is the same idea on the Canadian basis

Forwards fall out of the same primitive as a ratio, which is why they cannot disagree with the spot rates above.

DF(t1,t2) = DF(t2) DF(t1) , fcont = lnDF(t1,t2) (d2d1)/365
The forward discount factor telescopes, so compounding the forward from today to t1 and then to t2 returns DF(t2) exactly. The test suite asserts this identity rather than trusting it.

What the build refuses to publish

The formulas are only worth as much as the data flowing through them, so the daily job validates before it writes. A curve has to have strictly decreasing discount factors, which is the same statement as no negative forward rate anywhere on it, and every stored zero rate has to round-trip against its own discount factor. The new curve is then diffed against the previous day's snapshot, and a move beyond the threshold at any tenor fails the run. A market that fails keeps its previous file and the Action goes red. Nothing broken reaches the site quietly.

Each accepted snapshot is kept under its own date rather than overwriting one file, so the output is a time series you can query, not just a live read.

The math above is tested rather than asserted. The par-bond recursion is checked against its closed form, every convention is round-tripped back to the discount factor it came from, the forward identity is verified, and the browser port is lifted out of the shipped JavaScript and run against the Python engine on the live snapshots so the two cannot drift apart. The suite runs on every push.

The refresh job runs that suite before it is allowed to touch the data. A failing test stops the run, so a snapshot cannot reach this site unless the suite passed on the same commit that produced it. The status below is read from the published snapshot itself rather than from a build server, which is the same principle as the rest of the page: show the artefact, not a claim about it.

05 Provenance

Go to the desk that publishes the number

SOFR first came into the engine through FRED, which was convenient and wrong by a step. FRED redistributes SOFR. The New York Fed administers it. Moving one link up the chain cost an afternoon and bought three things: the full published precision, five decimals on the Averages where the redistributed copy had rounded them away; same-morning data instead of a lag of several days; and the official SOFR Index alongside the revision flags that tell you when a print has been restated.

There is a quiet test for whether a curve respects its inputs. Ask this one for the 180-day rate on a simple ACT/360 basis and it hands back the New York Fed's published 180-day Average, unchanged. The discount factor is not an approximation of the official number, it is the official number wearing a different convention. Everything the engine derives at that horizon inherits the same fidelity.

That precision also sharpened where honesty begins. The overnight curve is real published data out to 180 days, so a six-month option is priced off an actual rate, not an inference. Beyond 180 days the engine has no OIS swap quotes to bootstrap from, and it says so rather than quietly extending a line. Knowing exactly where the evidence stops is the part most tools leave out.

A number is only as good as the distance between you and whoever computed it.

SOFR, the SOFR Averages and the SOFR Index come from the Federal Reserve Bank of New York; U.S. Treasury par yields from FRED (Federal Reserve Bank of St. Louis); Canadian benchmark yields, Treasury bills and CORRA from the Bank of Canada. Refreshed every business day. For educational use, not investment advice. The overnight-anchored curve beyond its 180-day anchor is a labelled proxy, not a bootstrapped OIS discount curve.

Get the rate in the right convention

Pick a market and a maturity, and read the effective risk-free rate the way your model expects it.