Table of Contents

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.