Interest Rate Swap Valuation and Applications › Determining the Par Swap Rate
Quantitative FinanceAll chapters Interest Rate Swap Valuation and Applications › Determining the Par Swap RateOne section · Sections in this chapter8 / 17 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
To calculate a par quote for a new swap, find the fixed coupon that makes its initial model value zero under the specified payment, floating-index and discounting conventions. A client can also request an off-market coupon, in which case a nonzero initial value may be offset by an upfront payment. The calculation below uses discount factors to obtain the par coupon in the chapter's single-curve model.
For a par swap at inception, fixed and floating coupon PVs are equal. Equivalently, after adding equal hypothetical principal repayments, the corresponding fixed- and floating-rate bonds both have value N under the matching single-curve reset assumptions.
def calculate_par_swap_rate(discount_factors, maturities, payment_freq=1.0):
"""Par rate for a complete regular coupon grid in a single curve.
payment_freq is the common accrual interval in years. Every coupon date
must appear, not only quoted curve pillars. Stubs/day-count variation
and separate projection curves require a more general implementation.
"""
discounts = np.asarray(discount_factors, dtype=float)
times = np.asarray(maturities, dtype=float)
delta = float(payment_freq)
if (
discounts.ndim != 1
or times.ndim != 1
or not len(times)
or len(discounts) != len(times)
or not np.isfinite(delta)
or delta <= 0
or not np.all(np.isfinite(discounts))
or np.any(discounts <= 0)
or not np.all(np.isfinite(times))
or not np.allclose(
times, delta * np.arange(1, len(times) + 1), rtol=0, atol=1e-10
)
):
raise ValueError("Provide positive factors at every regular coupon date.")
annuity = delta * np.sum(discounts)
return (1 - discounts[-1]) / annuity
# Calculate par rates for different tenors
tenors = [1, 2, 3, 5, 7, 10]
calculated_par_rates = []
for i, tenor in enumerate(tenors):
# Reprice every annual coupon date, not just quoted maturity nodes.
payment_times = np.arange(1, int(tenor) + 1, dtype=float)
payment_zeros = np.interp(payment_times, market_maturities, boot_zero_rates)
payment_discounts = np.exp(-payment_zeros * payment_times)
par_rate = calculate_par_swap_rate(payment_discounts, payment_times)
calculated_par_rates.append(par_rate)Par Swap Rate Calculation
==================================================
Tenor Market Rate Calculated
--------------------------------------------------
1 yr 4.7000% 4.7000%
2 yr 5.0000% 5.0000%
3 yr 5.2000% 5.2000%
5 yr 5.5000% 5.5000%
7 yr 5.7000% 5.7000%
10 yr 5.8000% 5.8000%
Full annual coupon repricing should recover each input quote to numerical tolerance.The calculated rates reprice the hypothetical input quotes using the full annual coupon schedule and the same interpolation policy as calibration. Remaining numerical differences reflect solver and floating-point tolerance. Different contractual schedules require their own payment dates and accrual fractions; changing frequency is a separate calculation, not an explanation for roundoff.