Simulate the Volume-Weighted Median calculation used by the NY Fed.
Transaction Bucket A
Transaction Bucket B
Transaction Bucket C
Transaction Bucket D
Transaction Bucket E
Total Volume:
Median Volume Threshold (50%):
Calculated SOFR Rate:
How is SOFR Rate Calculated?
The Secured Overnight Financing Rate (SOFR) is a broad measure of the cost of borrowing cash overnight collateralized by Treasury securities. Unlike LIBOR, which was often based on estimates, SOFR is based on actual transaction data from the Treasury repurchase (repo) market.
The Calculation Formula: Volume-Weighted Median
The Federal Reserve Bank of New York calculates SOFR every business day. The specific methodology used is a Volume-Weighted Median. This method is chosen because it is robust against outliers and manipulation.
Here is the step-by-step logic used to calculate the rate:
Data Collection: The Fed collects transaction data from three sources: the Tri-party Repo Market, the General Collateral Finance (GCF) Repo Market, and bilateral Treasury repo transactions cleared through FICC.
Ordering: All valid transactions are ordered from the lowest interest rate to the highest interest rate.
Volume Aggregation: The total volume (in dollars) of all transactions is summed up.
Finding the Median: The calculator identifies the 50th percentile of the volume. This is the point where half of the money borrowed was at a rate lower than or equal to this rate, and half was higher.
Determination: The rate associated with the transaction that crosses this 50% cumulative volume threshold becomes the SOFR for that day.
Why Volume-Weighted Median?
A simple average (mean) can be skewed by a single small transaction with an extremely high or low interest rate. A volume-weighted average (VWAP) is better, but still susceptible to skew from large outliers.
The Volume-Weighted Median ensures that the rate represents the center of the market activity. To move the SOFR rate significantly, a market participant would have to execute a massive volume of transactions, making it much harder to manipulate than previous benchmarks.
Example Calculation
Consider the data used in the simulator above:
Transaction A: $200B at 5.30%
Transaction B: $450B at 5.32%
Transaction C: $150B at 5.29%
To find the rate, we first sort by rate (lowest to highest):
5.29% ($150B)
5.30% ($200B)
5.32% ($450B)
Total Volume = $800B. The 50th percentile is at $400B.
The first $150B is at 5.29%. We need $250B more to reach $400B. The next bucket (5.30%) contains $200B. Now we have accounted for $350B total. We still haven't reached $400B. We move to the next bucket (5.32%). The threshold crosses inside this bucket. Therefore, the SOFR would be 5.32%.
function calculateSOFR() {
// 1. Get Inputs
var r1 = parseFloat(document.getElementById('rate1').value);
var v1 = parseFloat(document.getElementById('vol1').value);
var r2 = parseFloat(document.getElementById('rate2').value);
var v2 = parseFloat(document.getElementById('vol2').value);
var r3 = parseFloat(document.getElementById('rate3').value);
var v3 = parseFloat(document.getElementById('vol3').value);
var r4 = parseFloat(document.getElementById('rate4').value);
var v4 = parseFloat(document.getElementById('vol4').value);
var r5 = parseFloat(document.getElementById('rate5').value);
var v5 = parseFloat(document.getElementById('vol5').value);
// Validation
if (isNaN(r1) || isNaN(v1) || isNaN(r2) || isNaN(v2) || isNaN(r3) || isNaN(v3) ||
isNaN(r4) || isNaN(v4) || isNaN(r5) || isNaN(v5)) {
alert("Please enter valid numbers for all rates and volumes.");
return;
}
// 2. Create Data Array
var transactions = [
{ rate: r1, vol: v1 },
{ rate: r2, vol: v2 },
{ rate: r3, vol: v3 },
{ rate: r4, vol: v4 },
{ rate: r5, vol: v5 }
];
// 3. Sort by Rate (Lowest to Highest)
transactions.sort(function(a, b) {
return a.rate – b.rate;
});
// 4. Calculate Total Volume
var totalVolume = 0;
for (var i = 0; i < transactions.length; i++) {
totalVolume += transactions[i].vol;
}
// 5. Find Median Threshold (50% of Volume)
var medianThreshold = totalVolume / 2;
// 6. Iterate to find the rate crossing the threshold
var cumulativeVolume = 0;
var sofrRate = 0;
var breakdownText = "Logic Trace:Sorted Transactions:";
for (var i = 0; i < transactions.length; i++) {
var prevVol = cumulativeVolume;
cumulativeVolume += transactions[i].vol;
breakdownText += (i+1) + ". Rate: " + transactions[i].rate.toFixed(2) + "% | Vol: $" + transactions[i].vol + "B | Cumulative: $" + cumulativeVolume.toFixed(0) + "B";
if (prevVol = medianThreshold) {
sofrRate = transactions[i].rate;
breakdownText += "Threshold Crossed! The cumulative volume crosses $" + medianThreshold.toFixed(0) + "B at the rate of " + sofrRate.toFixed(2) + "%.";
}
}
// 7. Display Results
document.getElementById('res-total-vol').innerHTML = "$" + totalVolume.toLocaleString() + " Billion";
document.getElementById('res-median-vol').innerHTML = "$" + medianThreshold.toLocaleString() + " Billion";
document.getElementById('res-sofr').innerHTML = sofrRate.toFixed(2) + "%";
document.getElementById('res-explanation').innerHTML = breakdownText;
document.getElementById('result-area').style.display = "block";
}