A par swap coupon from discounted cashflows

A par swap has a fixed coupon that balances the discounted floating leg. We calculate that coupon for a five-year receive-fixed USD swap, compare it with the native fair-rate result and reprice the contract at the solved rate.

The trade starts on 24 February 2026 with USD 1 million notional. Fixed payments are semiannual, floating payments quarterly, dates unadjusted and accrual Actual/360. A single SOFR curve supplies projection and discounting. For this spot-starting setup, the floating leg telescopes to

\[V(K)=N\left[KA-(1-P(0,T))\right],\qquad A=\sum_i\tau_iP(0,T_i),\qquad K^*=\frac{1-P(0,T)}{A}.\]

Here \(A\) is the annuity per unit notional, measured in years. With separate projection and discount curves, calculate the floating-leg sum explicitly. Setup.

Computational study. Canonical notebook · Companion files and setup. Download this notebook.

This page renders saved outputs; the documentation build does not execute it. Run the canonical notebook from its own directory with the checkout-built binding to reproduce the computation. Each study records its market inputs and numerical assumptions.

import pandas as pd
import quantsupport as qs
from market_data import load_market

market = load_market()
ref = market.quotes.reference_date
notional = 1_000_000
agreed_coupon = 0.04
fixed_period_months = 6
number_of_periods = 10

Solving and repricing the fixed coupon

We build a receive-fixed trade for each candidate coupon, keeping the dates, notional and conventions fixed. The payment schedule also gives an independent annuity calculation. Once the native pricer supplies its par coupon, we construct a new trade at that rate and check its value.

def receive_fixed_swap(fixed_rate):
    instrument = qs.Swap(
        identifier="USD-SWAP-5Y",
        start_date=ref,
        maturity_date=ref + "5Y",
        notional=notional,
        fixed_rate=fixed_rate,
        currency=qs.Currency.USD,
        market_index=qs.MarketIndex.SOFR,
        day_counter=qs.DayCounter.Actual360,
        fixed_leg_frequency=qs.Frequency.Semiannual,
        floating_leg_frequency=qs.Frequency.Quarterly,
    )
    return qs.Trade(instrument, ref, qs.Side.LongReceive)


context = qs.PricingContext(
    quotes=market.quotes, curves=market.curves, csa_policy=market.csa_policy
)
with context:
    curve = context.curve(qs.MarketIndex.SOFR)
    payment_dates = [
        ref + qs.Period(fixed_period_months * period, qs.TimeUnit.Months)
        for period in range(number_of_periods + 1)
    ]
    fixed_periods = []
    for start, end in zip(payment_dates, payment_dates[1:]):
        accrual = qs.DayCounter.Actual360.year_fraction(start, end)
        discount_factor = curve.discount_factor(end)
        fixed_periods.append(
            {
                "payment_date": str(end),
                "accrual": accrual,
                "discount_factor": discount_factor,
            }
        )
    fixed_schedule = pd.DataFrame(fixed_periods)
    annuity = (fixed_schedule["accrual"] * fixed_schedule["discount_factor"]).sum()
    floating_pv_per_notional = 1 - curve.discount_factor(ref + "5Y")
    independently_solved_rate = floating_pv_per_notional / annuity
    swap_result = context.evaluate(
        receive_fixed_swap(agreed_coupon), [qs.Request.Value, qs.Request.FairRate(0)]
    )
    par_result = context.evaluate(
        receive_fixed_swap(swap_result.fair_rate), [qs.Request.Value]
    )

pd.Series(
    {
        "fixed_annuity_years": annuity,
        "native_par_rate": swap_result.fair_rate,
        "independent_par_rate": independently_solved_rate,
        "original_pv_usd": swap_result.price,
        "independent_original_pv_usd": notional
        * (agreed_coupon * annuity - floating_pv_per_notional),
        "pv_at_par_usd": par_result.price,
    }
)
fixed_annuity_years            4.524195e+00
native_par_rate                4.036802e-02
independent_par_rate           4.036800e-02
original_pv_usd               -1.664991e+03
independent_original_pv_usd   -1.664892e+03
pv_at_par_usd                  5.366019e-11
dtype: float64
print(
    f"Par coupon: {swap_result.fair_rate:.6%}; PV at par: USD {par_result.price:.3e}."
)
print(f"One coupon bp changes fixed-leg PV by USD {notional * annuity * 1e-4:.2f}.")
fixed_schedule
Par coupon: 4.036802%; PV at par: USD 5.366e-11.
One coupon bp changes fixed-leg PV by USD 452.42.
payment_date accrual discount_factor
0 2026-08-24 0.502778 0.976544
1 2027-02-24 0.511111 0.953257
2 2027-08-24 0.502778 0.933651
3 2028-02-24 0.511111 0.914227
4 2028-08-24 0.505556 0.897578
5 2029-02-24 0.511111 0.881127
6 2029-08-24 0.502778 0.864875
7 2030-02-24 0.511111 0.848652
8 2030-08-24 0.502778 0.832992
9 2031-02-24 0.511111 0.817367

The annuity also measures exposure to the contractual coupon: at a fixed market, increasing that coupon by one basis point changes PV by notional × annuity × 1e-4. Market-quote risk additionally changes projected floating coupons and discount factors. The portfolio-risk study examines those dependencies; currency conversion extends the valuation to CLP payments under USD collateral.