Strasmore Research
Learn am Matt ConnorBy Matt Connor

Risk-free rate for Sharpe ratio: how to match am

Risk-free rate dey change over time. Learn how to match bill yield with your return frequency, subtract am period by period, and see wetin one fixed rate fit cost.

Risk-free rate wey dey inside Sharpe ratio na the return wey you for fit collect over that same period without taking the risk wey you dey grade. Na series be this, with one reading for each period, and dem dey quote am for the same currency as the returns. Match the frequency with the return frequency, then subtract am period by period. Annualize only once, for the end.

Na when people skip this procedure most published Sharpe ratios dey go wrong. If you carry one number from the top of twenty-year return history — usually wetin Treasury bill dey pay this week — you dey charge 2009 the same hurdle as 2024. Our guide to the Sharpe ratio explain wetin the ratio dey measure. This page cover the input wey almost nobody dey document.

Risk-free rate no be constant

Sharpe ratio wey you measure over twenty years dey cover rate history. The panel below use three-month Treasury bill and reduce each calendar year to one average plus the range around am.

QueryThree-month Treasury bill yield by calendar year
The exact SQL behind every number
SELECT
    toString(toYear(date))                  AS year,
    round(avg(toFloat64(yield_3_month)), 2) AS avg_bill_pct,
    round(min(toFloat64(yield_3_month)), 2) AS low_bill_pct,
    round(max(toFloat64(yield_3_month)), 2) AS high_bill_pct
FROM global_markets.treasury_yields
WHERE date >= '2005-01-01'
  AND yield_3_month IS NOT NULL
GROUP BY year
ORDER BY year
Run this yourself

The yearly average run 3.22% for 2005 and 3.74% for 2026, across 22 calendar years. The path between those two readings na the part wey matter. The line drop reach floor, remain there for most of one decade, then climb back. The low and high columns show how far the rate travel inside individual years. Na why even one-year Sharpe ratio need matched series instead of one quote.

Which risk-free rate belong inside Sharpe ratio

Four properties dey determine the choice.

  1. Short maturity. The rate must dey almost certain over one measurement period. Three-month bill qualify for monthly work. Ten-year note no qualify, because e price fit move during the month wey you dey measure.
  2. The return own currency. If strategy dey denominated in euro, grade am against euro bill.
  3. The return own frequency. Dem quote bill yields as annual rates, so you must convert annual quote down before e touch monthly return.
  4. Comparable return definitions for both sides. Bill yield na the full return, so strategy return wey exclude dividends no dey measured on the same basis. Price return versus total return cover that difference.

The conversion for point three get two conventions wey people dey use. Simple division split annual quote by twelve. Compounding take the twelfth root of one plus annual rate. For hypothetical 5% bill, those methods give 0.4167% and 0.4074% for the month. Difference na about one basis point, and e repeat every month for the sample. The panels here use compounding. If bill yields interest you as holding instead of hurdle, dividend yield versus Treasury yields and where to park idle cash cover that matter.

How to build monthly excess return series

The construction dey mechanical: take the last close for each month, calculate the month return, convert that month bill quote to monthly rate, then subtract. The result na excess return series, with one number for each month. How monthly returns are measured explain the return side in detail.

QueryMonthly return, monthly risk-free rate and the excess, 2020 to 2024
The exact SQL behind every number
WITH
    monthly_px AS
    (
        SELECT
            toStartOfMonth(date)           AS m,
            argMax(toFloat64(close), date) AS month_close
        FROM global_markets.stocks_daily_aggs
        WHERE ticker = 'SPY'
          AND date >= '2019-12-01'
          AND date <  '2025-01-01'
        GROUP BY m
    ),
    prior_px AS
    (
        SELECT
            addMonths(m, 1) AS m,
            month_close     AS prev_close
        FROM monthly_px
    ),
    monthly_rf AS
    (
        SELECT
            toStartOfMonth(date)                                       AS m,
            pow(1 + avg(toFloat64(yield_3_month)) / 100, 1.0 / 12) - 1 AS rf_month
        FROM global_markets.treasury_yields
        WHERE date >= '2019-12-01'
          AND date <  '2025-01-01'
          AND yield_3_month IS NOT NULL
        GROUP BY m
    )
SELECT
    toString(cur.m)                                                      AS month,
    round(100 * (cur.month_close / prv.prev_close - 1), 2)               AS spy_ret_pct,
    round(100 * rf.rf_month, 3)                                          AS rf_pct,
    round(100 * (cur.month_close / prv.prev_close - 1 - rf.rf_month), 2) AS excess_pct
FROM monthly_px AS cur
INNER JOIN prior_px AS prv ON prv.m = cur.m
INNER JOIN monthly_rf AS rf ON rf.m = cur.m
ORDER BY cur.m
Run this yourself

Those na SPY closes, so return column na price return with dividends excluded. Risk-free column open at 0.128% for the first month wey dey show and close at 0.359%. Beside equity months wey fit swing several percent, risk-free strip look like rounding. Across 60 months of subtraction, na the full difference between two Sharpe ratios wey claim to measure the same thing.

Subtract first, then annualize

Once the series align, two operation orders dey available, and dem no give the same result. Excess first: subtract monthly rate from monthly return, then annualize the excess series. Annualize first: compound returns into annual figure, compound bill into annual figure, then subtract one from the other.

QueryAnnualized risk premium: subtract monthly, or annualize each leg first
The exact SQL behind every number
WITH
    monthly_px AS
    (
        SELECT
            toStartOfMonth(date)           AS m,
            argMax(toFloat64(close), date) AS month_close
        FROM global_markets.stocks_daily_aggs
        WHERE ticker = 'SPY'
          AND date >= '2004-12-01'
          AND date <  '2025-01-01'
        GROUP BY m
    ),
    prior_px AS
    (
        SELECT
            addMonths(m, 1) AS m,
            month_close     AS prev_close
        FROM monthly_px
    ),
    monthly_rf AS
    (
        SELECT
            toStartOfMonth(date)                                       AS m,
            pow(1 + avg(toFloat64(yield_3_month)) / 100, 1.0 / 12) - 1 AS rf_month
        FROM global_markets.treasury_yields
        WHERE date >= '2004-12-01'
          AND date <  '2025-01-01'
          AND yield_3_month IS NOT NULL
        GROUP BY m
    ),
    excess AS
    (
        SELECT
            cur.m                                             AS m,
            cur.month_close / prv.prev_close - 1              AS ret,
            rf.rf_month                                       AS rf_month,
            cur.month_close / prv.prev_close - 1 - rf.rf_month AS exc
        FROM monthly_px AS cur
        INNER JOIN prior_px AS prv ON prv.m = cur.m
        INNER JOIN monthly_rf AS rf ON rf.m = cur.m
    )
SELECT
    concat(toString(intDiv(count(), 12)), '-year')            AS horizon,
    round(100 * (exp(12 * avg(log(1 + exc))) - 1), 2)         AS excess_first_pct,
    round(100 * ((exp(12 * avg(log(1 + ret))) - 1)
               - (exp(12 * avg(log(1 + rf_month))) - 1)), 2)  AS annualize_first_pct,
    round(100 * ((exp(12 * avg(log(1 + ret))) - 1)
               - (exp(12 * avg(log(1 + rf_month))) - 1)
               - (exp(12 * avg(log(1 + exc))) - 1)), 3)       AS gap_pct
FROM excess
CROSS JOIN (SELECT arrayJoin([1, 3, 5, 10, 20]) AS years) AS hz
WHERE m >= subtractYears(toDate('2025-01-01'), years)
GROUP BY years
ORDER BY years
Run this yourself

Across the 20-year window, excess-first answer na 6.49% per year against 6.59% for the other order. Difference na 0.099 percentage points. For 1-year window, the same two calculations dey 0.814 points apart. Annualize-first figure dey above excess-first figure for every window here. The difference come from a cross term. When you compound return series into annual number, the calculation include earning bill rate on the risk premium itself. If you subtract annualized bill afterwards, you no remove that part. Cross term dey scale with the product of the two annualized numbers, so e widest when bills dey pay well.

Annualizing get another fork. Arithmetic annualization multiply average monthly excess by twelve. Geometric annualization compound am: na the constant annual rate wey reproduce cumulative result. Imagine hypothetical two-period run of +50% followed by -33.3%, wey end exactly where e start. Arithmetic average na +8.35% per period, while geometric average na zero. The panels here use compounding. One footnote matter: under arithmetic annualization, twelve times mean return minus twelve times mean bill rate equal twelve times mean excess algebraically. Under that convention, subtraction order no change the numerator. But denominator still move, because standard deviation of excess returns separate from standard deviation of raw returns once the rate itself dey move.

Wetin one fixed rate dey cost the answer

The common shortcut na to take current bill yield and subtract am from every period for the history. The panel below calculate Sharpe ratio both ways across five windows wey end on the same date: once against matched monthly series, and once against bill yield wey dey apply at the end of each window.

QuerySharpe ratio on a matched rate series against one fixed rate
The exact SQL behind every number
WITH
    monthly_px AS
    (
        SELECT
            toStartOfMonth(date)           AS m,
            argMax(toFloat64(close), date) AS month_close
        FROM global_markets.stocks_daily_aggs
        WHERE ticker = 'SPY'
          AND date >= '2004-12-01'
          AND date <  '2025-01-01'
        GROUP BY m
    ),
    prior_px AS
    (
        SELECT
            addMonths(m, 1) AS m,
            month_close     AS prev_close
        FROM monthly_px
    ),
    monthly_rf AS
    (
        SELECT
            toStartOfMonth(date)                                       AS m,
            pow(1 + avg(toFloat64(yield_3_month)) / 100, 1.0 / 12) - 1 AS rf_month
        FROM global_markets.treasury_yields
        WHERE date >= '2004-12-01'
          AND date <  '2025-01-01'
          AND yield_3_month IS NOT NULL
        GROUP BY m
    ),
    excess AS
    (
        SELECT
            cur.m                                AS m,
            cur.month_close / prv.prev_close - 1 AS ret,
            rf.rf_month                          AS rf_month
        FROM monthly_px AS cur
        INNER JOIN prior_px AS prv ON prv.m = cur.m
        INNER JOIN monthly_rf AS rf ON rf.m = cur.m
    )
SELECT
    concat(toString(intDiv(count(), 12)), '-year')                             AS horizon,
    round(sqrt(12) * avg(ret - rf_month) / stddevSamp(ret - rf_month), 2)      AS sharpe_matched_rf,
    round(sqrt(12) * avg(ret - rf_fixed) / stddevSamp(ret - rf_fixed), 2)      AS sharpe_fixed_rf,
    round(abs(sqrt(12) * avg(ret - rf_month) / stddevSamp(ret - rf_month)
            - sqrt(12) * avg(ret - rf_fixed) / stddevSamp(ret - rf_fixed)), 2) AS abs_gap
FROM excess
CROSS JOIN
(
    SELECT pow(1 + avg(toFloat64(yield_3_month)) / 100, 1.0 / 12) - 1 AS rf_fixed
    FROM global_markets.treasury_yields
    WHERE date >= '2024-12-01'
      AND date <  '2025-01-01'
      AND yield_3_month IS NOT NULL
) AS fixed_rate
CROSS JOIN (SELECT arrayJoin([1, 3, 5, 10, 20]) AS years) AS hz
WHERE m >= subtractYears(toDate('2025-01-01'), years)
GROUP BY years
ORDER BY years
Run this yourself

For 1-year window, the two readings land 0.07 apart. Over twelve months, fixed rate get small space to move away from the window own average. For 20-year window, dem dey 0.18 apart: 0.49 on matched series against 0.32 on fixed rate. The error follow the gap between the one rate wey you choose and the average rate wey actually apply. That gap get more room to widen as the window become longer. Anything downstream inherit am, and volatility targeting and position sizing dey run on inputs exactly like this.

The currency trap

If strategy earn in one currency but you grade am against bill for another currency, the result no get much meaning for either side. Calculate excess return inside one currency: euro returns against euro bill, yen returns against yen bill. Hedged strategies get another trap. Currency hedge price dey come from forward, and forward points dey close to interest rate difference between the two currencies. That already put part of domestic rate inside hedged foreign return. If you subtract foreign bill on top, you charge the same rate twice. Put the currency beside the rate and the ambiguity disappear.

Publish the rate together with the ratio

Nobody fit check Sharpe ratio wey you quote without risk-free source and frequency. Two desks fit calculate 0.8 and 1.1 from the same return stream, and both fit dey correct if dem use different rate series and conversion conventions. Four items must follow the number: instrument wey provide the rate, its frequency, conversion convention, and exact window. Na the difference between number wey reader fit audit and number wey reader must accept on faith.

Data notes and reproducibility

The rate series for every panel na three-month Treasury bill from daily Treasury yield curve. Dem average am inside each calendar month, then convert am to monthly rate as the twelfth root of one plus annual quote. Returns na SPY month-end closes, price return only, with dividends excluded. The ratios use sample standard deviation of monthly excess returns and square root of twelve for annualization. The last two panels use windows wey end on one common date, and fixed-rate column take average bill quote for the final month of each window. Every figure for the prose above come from the panel beside am.

FAQ

Which risk-free rate should I use in the Sharpe ratio?

Short government bill for the same currency as the returns, converted to the same frequency as the returns. For monthly USD returns, that means one-month or three-month Treasury bill. Longer maturities carry price risk over the measurement period, so dem no qualify as risk-free leg.

Can I subtract today's Treasury bill yield from a long return history?

Na the most common mistake for published Sharpe ratios. Over the 20-year window above, fixed end-of-window rate and matched monthly series produce readings wey dey 0.18 apart on the exact same returns.

Do I annualize the risk-free rate before subtracting it?

No. Convert annual bill quote down to the return frequency, subtract period by period, then annualize excess series once for the end. If you annualize each leg separately and subtract afterwards, cross term go remain inside the answer.

How much does the risk-free rate move a Sharpe ratio?

Roughly, na change in the rate divided by annualized volatility. For hypothetical strategy with 15% annualized volatility, 4% difference in assumed rate move the ratio by about 0.27. Na the distance between ordinary track record and celebrated one.

What is the difference between arithmetic and geometric annualization?

Arithmetic multiply average period excess by number of periods in one year. Geometric compound the periods into constant rate wey reproduce cumulative result. Per period, geometric figure dey at or below arithmetic one, and the gap between dem dey widen as volatility rise.


Every panel here carry the SQL wey produce am, so both rate series and window dey visible. If you want run the same construction on another ticker or another stretch of history, ask for am in plain English on the Strasmore terminal.

#sharpe ratio#risk-free rate#excess returns#annualization#performance measurement