Pricing a USD SOFR swap to match the market
QuantLib's MakeOIS builds an overnight swap from an index and a tenor, taking a default for every option it isn't
given. For USD SOFR most of its defaults are already the market's. This example sets the market's conventions in XML,
adds them one at a time, and shows what each changes in the price.
The quotes are illustrative, made up for the example. The conventions are the market's, as the CFTC describes USD SOFR OIS for its trade execution requirement (Release 8745-23, 2023).
The market's convention
| The market | QuantLib's MakeOIS defaults |
|
|---|---|---|
| Start | spot, two New York business days after the trade | two days on SOFR's fixing calendar |
| Both legs | annual, Actual/360 | annual, Actual/360 |
| Business days | New York's | SOFR's fixing calendar, the US government securities market's |
| Payment | two business days after each period ends | at the period's end |
| SOFR fixings | on the US government securities calendar | the same |
So the defaults already match the market's frequency, day count and spot lag. Two things differ: the payment lag, and the calendar. New York's business days are the Federal Reserve's, and the government securities calendar closes on days the Federal Reserve doesn't, Good Friday among them.
The conventions file holds the market's convention, USD-SOFR, and the two steps toward it from the defaults:
<!--
SPDX-FileCopyrightText: 2026 The nanoql Authors
SPDX-License-Identifier: Apache-2.0
-->
<!--
USD SOFR OIS as the CFTC's 2023 trade execution table specifies it: spot (T+2), both legs annual on ACT/360, New York
business days (USNY), paid two days after each period ends; SOFR fixed on the US government securities calendar.
USNY maps New York's banking days to the Federal Reserve's calendar. "Lag" and "LagAndCalendars" are the steps from
QuantLib's MakeOIS defaults toward the market's convention; "USD-SOFR-Dated" is the market's convention without its
settlement days, for a swap given its own start date.
-->
<Conventions xmlns="https://nanoql.org/schemas/conventions">
<Alias name="USNY" base="UnitedStates.FederalReserve" />
<OisConvention name="USD-SOFR">
<Index>Sofr</Index>
<SettlementDays>2</SettlementDays>
<SettlementCalendar>USNY</SettlementCalendar>
<PaymentLag>2</PaymentLag>
<PaymentFrequency>Annual</PaymentFrequency>
<PaymentCalendar>USNY</PaymentCalendar>
<FixedLegCalendar>USNY</FixedLegCalendar>
<FixedLegDayCounter>ACT/360</FixedLegDayCounter>
<OvernightLegCalendar>USNY</OvernightLegCalendar>
</OisConvention>
<OisConvention name="Lag">
<Index>Sofr</Index>
<PaymentLag>2</PaymentLag>
</OisConvention>
<OisConvention name="LagAndCalendars">
<Index>Sofr</Index>
<SettlementCalendar>USNY</SettlementCalendar>
<PaymentLag>2</PaymentLag>
<PaymentCalendar>USNY</PaymentCalendar>
<FixedLegCalendar>USNY</FixedLegCalendar>
<OvernightLegCalendar>USNY</OvernightLegCalendar>
</OisConvention>
<OisConvention name="USD-SOFR-Dated">
<Index>Sofr</Index>
<SettlementCalendar>USNY</SettlementCalendar>
<PaymentLag>2</PaymentLag>
<PaymentFrequency>Annual</PaymentFrequency>
<PaymentCalendar>USNY</PaymentCalendar>
<FixedLegCalendar>USNY</FixedLegCalendar>
<FixedLegDayCounter>ACT/360</FixedLegDayCounter>
<OvernightLegCalendar>USNY</OvernightLegCalendar>
</OisConvention>
</Conventions>
The curve
The curve is bootstrapped from nine quotes, 15 October 2026, each a SOFR swap built by the market's convention:
var today = new Date(15, Month.October, 2026);
var context = new EvaluationContext { EvaluationDate = today };
(Period Tenor, double Rate)[] quotes =
[
(new(1, TimeUnit.Months), 0.0430), (new(3, TimeUnit.Months), 0.0428), (new(6, TimeUnit.Months), 0.0421),
(new(1, TimeUnit.Years), 0.0405), (new(2, TimeUnit.Years), 0.0380), (new(3, TimeUnit.Years), 0.0368),
(new(5, TimeUnit.Years), 0.0360), (new(7, TimeUnit.Years), 0.0362), (new(10, TimeUnit.Years), 0.0370),
];
ConventionSet set = ConventionSet.QuantLib.With(ConventionsXml.Read(conventionsFile));
OisConvention market = set.Ois("USD-SOFR");
OISRateHelper[] helpers = [.. quotes.Select(q => market.RateHelper(q.Rate, q.Tenor, context))];
var curve = new PiecewiseLogLinearDiscount(today, helpers, new Actual365Fixed(), context);
Each convention's price
Three five-year swaps on $10 million: one at the curve's par rate, one off-market at 4.25%, and one at par starting on
Good Friday, 26 March 2027. Each is priced four times, from MakeOIS's defaults to the market's convention:
(string Step, MakeOIS Builder)[] steps =
[
("QuantLib's MakeOIS defaults", new MakeOIS(tenor, set.OvernightIndex("Sofr", curve))),
("+ the 2-day payment lag", set.Ois("Lag").Builder(tenor, curve)),
("+ New York business days", set.Ois("LagAndCalendars").Builder(tenor, curve)),
("the market's convention", (start is null ? market : set.Ois("USD-SOFR-Dated")).Builder(tenor, curve)),
];
foreach (var (step, builder) in steps)
{
OvernightIndexedSwap swap = builder.WithEffectiveDate(start).WithFixedRate(rate).WithNominal(10e6)
.ToOvernightIndexedSwap(context);
OvernightIndexedSwapResults result = DiscountingSwapEngine.Calculate(swap, context, curve);
rows.Add((trade, step, result.NPV!.Value, result.FairRate!.Value));
}
| Trade | Step | NPV | Fair rate | Step's difference |
|---|---|---|---|---|
| par, spot | QuantLib's MakeOIS defaults | -3.11 | 3.6000% | |
| par, spot | + the 2-day payment lag | 0.00 | 3.6000% | 3.11 |
| par, spot | + New York business days | 0.00 | 3.6000% | 0.00 |
| par, spot | the market's convention | 0.00 | 3.6000% | 0.00 |
| off-market 4.25%, spot | QuantLib's MakeOIS defaults | -295,785.18 | 3.6000% | |
| off-market 4.25%, spot | + the 2-day payment lag | -295,701.82 | 3.6000% | 83.36 |
| off-market 4.25%, spot | + New York business days | -295,701.82 | 3.6000% | 0.00 |
| off-market 4.25%, spot | the market's convention | -295,701.82 | 3.6000% | 0.00 |
| par, from Good Friday 2027 | QuantLib's MakeOIS defaults | -26,181.32 | 3.5416% | |
| par, from Good Friday 2027 | + the 2-day payment lag | -26,176.39 | 3.5416% | 4.93 |
| par, from Good Friday 2027 | + New York business days | -25,782.41 | 3.5425% | 393.98 |
| par, from Good Friday 2027 | the market's convention | -25,782.41 | 3.5425% | 0.00 |
- The payment lag moves every trade. Paying two days late discounts each period's net payment over two more days, so its effect grows with the gap between the fixed rate and the floating leg: small at par, larger off-market.
- The calendars move only the Good Friday trade. Its start, 26 March 2027, and its end, 26 March 2032, are both Good Fridays: on SOFR's fixing calendar both are holidays, and the schedule rolls them to the Monday; on New York's days both stand.
- The market's convention adds nothing beyond the two: its frequency, day count and spot lag are the defaults'.
The Good Friday trade takes
USD-SOFR-Dated, the same convention without the spot lag, since a swap given its own start date can't take settlement days too.
On the Good Friday trade, the first period accrues from a day SOFR isn't published. The fixing before it, Thursday's, is projected over its own value period, to the next fixing day, and accrues from the period's start, as QuantLib's overnight coupon does.
Conventions checked against market prices across many more trades are Masseef's verified catalogue.