SOFR Futures Fair Value & Implied Fed Path

A fair-value calculator for SOFR futures (SR3 contracts like SFRH6, SFRM6, SFRZ6) that produces the implied Fed funds path from the market price.

A SOFR future settles to the compounded daily SOFR over its 3-month reference quarter:

\[ \text{settle} = \left(\prod_{i \in \text{ref-quarter}} \left(1 + \frac{\text{SOFR}_i}{360} \cdot d_i\right) - 1\right) \cdot \frac{360}{D} \cdot 100 \]

So pricing the contract is really a question of forecasting daily SOFR through the quarter, which decomposes into (1) the daily SOFR–FF spread and (2) the path of fed funds itself.

How it works:

import pandas as pd
import pandas_market_calendars as mcal
from datetime import date


df = pd.read_csv("newsofr.csv").drop("PX_BID", axis=1)
sofr_df = df
sofr_df["Date"] = pd.to_datetime(df["Date"], errors="coerce").dt.date


begin_date = '2026-02-19'
ref_start_date = '2026-12-16'
ref_end_date= '2027-03-16'

def minus_one_day(day):
    return "-".join(day.split("-")[:-1] + [str(int(day.split("-")[-1]) - 1).zfill(2)])


if (int("".join(begin_date.split("-"))) - int("".join(ref_start_date.split("-"))) > 0):
    in_reference = True
    prev_days = mcal.get_calendar('NYSE').valid_days(start_date=ref_start_date, end_date=minus_one_day(begin_date))
    days = mcal.get_calendar('NYSE').valid_days(start_date=begin_date, end_date=ref_end_date)
else:
    in_reference = False
    prev_days = mcal.get_calendar('NYSE').valid_days(start_date=begin_date, end_date=minus_one_day(ref_start_date))
    days = mcal.get_calendar('NYSE').valid_days(start_date=ref_start_date, end_date=ref_end_date)

params = {
    "initialSFR": 3.73,
    "SER_FF": {
        "G": -2.875, "H": -3.25, "J": -2.75, "K": -2.75,
        "M": -3.25,  "N": -3.75, "Q": -3.75, "U": -4.75,
        "V": -4.25,  "X": -4.25, "Z": -6.25, "F": -6.25,
    },
    "starting_ff": 3.64,
    "ff_changes_prob": [
        (1, {
            date(2027, 3, 17): 0,
            date(2027, 4, 28): 0,
            date(2027, 6, 9): 0,
            date(2027, 1, 27): 0,
            date(2026, 6, 17): 0,
            date(2026, 7, 29): 0,
            date(2026, 3, 18): 0,
            date(2026, 4, 29): 0,
        }),
    ],
    "expected_value": False,
    "permutations": True,
    "values": [-0.25, 0, 0.25],
    "target": 3.12,
}


def get_contract_code(month):
    mapping = {1:'F', 2:'G', 3:'H', 4:'J', 5:'K', 6:'M',
               7:'N', 8:'Q', 9:'U', 10:'V', 11:'X', 12:'Z'}
    return mapping.get(month, "Invalid Month")

def get_day_rate(month, ff):
    ser_ff = params["SER_FF"][get_contract_code(month)]
    return (ff - (ser_ff/100))/100

def get_known_day_rate(day):
    return sofr_df.loc[sofr_df["Date"] == day.date()]["PX_LAST"].item()/100


def run_sim(ff_changes):
    current_ff = params["starting_ff"]
    running_total = 1

    if in_reference:
        # elapsed portion: use realized SOFR
        for j in range(len(prev_days)):
            prev_day = prev_days[j]
            if j != len(prev_days) - 1:
                effective_days = (prev_days[j + 1] - prev_day).days
            else:
                effective_days = (days[0] - prev_day).days
            day_rate = get_known_day_rate(prev_days[j])
            running_total *= 1 + (day_rate * effective_days)/360
            current_ff += ff_changes.get(prev_day.date(), 0)
    else:
        # pre-reference: walk forward applying FOMC changes
        for j in range(len(prev_days)):
            prev_day = prev_days[j]
            current_ff += ff_changes.get(prev_day.date(), 0)

    # forward portion: simulate using current FF + per-month SOFR-FF spread
    for i in range(len(days)):
        day = days[i]
        effective_days = (days[i + 1] - day).days if i != len(days) - 1 else 1
        day_rate = get_day_rate(day.month, current_ff)
        running_total *= 1 + (day_rate * effective_days)/360
        current_ff += ff_changes.get(day.date(), 0)

    total_days = ((days[-1] - prev_days[0]).days + 1) if in_reference else ((days[-1] - days[0]).days + 1)
    return (running_total - 1) * (360/total_days * 100)


def create_sim(contingencies, values):
    new_contingencies = []
    key_list = list(contingencies[0][1].keys())
    create_more(key_list, 0, dict(), values, new_contingencies)
    return new_contingencies


def create_more(key_list, idx, current_dict, values, new_contingencies):
    if idx == len(key_list):
        chance = 1/(len(values) ** len(key_list))
        new_contingencies.append((chance, current_dict))
        return
    for value in values:
        new_dict = current_dict.copy()
        new_dict[key_list[idx]] = value
        create_more(key_list, idx+1, new_dict, values, new_contingencies)


if __name__ == "__main__":
    for contingency in params["ff_changes_prob"]:
        for key in list(contingency[1].keys()):
            if (key - date.fromisoformat(ref_end_date)).days > 0:
                del contingency[1][key]

    contingencies = create_sim(params["ff_changes_prob"], params["values"]) \
        if params["permutations"] else params["ff_changes_prob"]

    expected_value = 0 if params["expected_value"] else []
    for contingency in contingencies:
        if params["expected_value"]:
            expected_value += run_sim(contingency[1]) * contingency[0]
        else:
            expected_value.append(run_sim(contingency[1]))

    # find scenarios that bracket the market price
    if not params["expected_value"]:
        closest_below = closest_above = float('inf')
        closest_below_idx = closest_above_idx = -1
        for i in range(len(expected_value)):
            diff = params["target"] - expected_value[i]
            if diff <= 0 and abs(diff) < closest_above:
                closest_above, closest_above_idx = abs(diff), i
            elif diff > 0 and abs(diff) < closest_below:
                closest_below, closest_below_idx = abs(diff), i

        print(f"Target: {params['target']}")
        if closest_above_idx != -1:
            print(f"Closest Above: {expected_value[closest_above_idx]}")
            print(f"  scenario: {contingencies[closest_above_idx][1]}")
        if closest_below_idx != -1:
            print(f"Closest Below: {expected_value[closest_below_idx]}")
            print(f"  scenario: {contingencies[closest_below_idx][1]}")