Skip to content
All library documents

Swap Par Rates Versus Forward Rates from a Yield Curve

Article Quant Q&A · Author: lcheng

Summary

This exchange distinguishes a swap’s par rate from a curve’s forward rate. The example builds a deposit and swap curve in QuantLib, then compares the market quote used for a ten-year swap with a swap fair rate and a rate returned by the curve’s forwardRate method. The swap fair rate approximately reproduces the input quote, while the forward rate does not, prompting the question of whether the curve method is being used incorrectly.

The answer explains that forwardRate gives the rate over one start-to-end period, without intermediate coupon payments. A compounded forward rate reflects reinvestment within that period; it is not the fixed coupon rate that makes a multi-payment swap’s value zero. To obtain par rates for different swap maturities, the suggested approach is to construct the corresponding swap schedules and calculate each instrument’s fair rate, optionally wrapping that work in a helper function. The response does not provide that implementation, and the example’s result depends on the chosen schedules, conventions, and curve setup.

Key ideas

  • A swap par rate is the fixed coupon that balances the present value of the swap’s legs.
  • A curve forward rate covers a single period and does not account for intermediate swap coupon payments.
  • A compounded forward rate represents reinvestment under its compounding convention.
  • Calculating par rates for other maturities requires valuing swaps with those maturities and conventions.
  • A helper function can reduce repeated swap construction, though the exchange does not supply its code.

Tags

Full text
# Use QuantLib Python to calculate yield curve par rates


# Use QuantLib Python to calculate yield curve par rates












I would want to use QuantLib Python to calculate par rates of a swap curve.

The following code is what I've done so far:

```
from QuantLib import *

# global data
calendar = TARGET()
todaysDate = Date(6,November,2001);
Settings.instance().evaluationDate = todaysDate
settlementDate = Date(8,November,2001);

# market quotes
deposits = { (1,Weeks): 0.0382,
             (1,Months): 0.0372,
             (3,Months): 0.0363,
             (6,Months): 0.0353,
             (9,Months): 0.0348,
             (1,Years): 0.0345 }

swaps = { (2,Years): 0.037125,
          (3,Years): 0.0398,
          (5,Years): 0.0443,
          (10,Years): 0.05165,
          (15,Years): 0.055175 }

# convert them to Quote objects
for n,unit in deposits.keys():
    deposits[(n,unit)] = SimpleQuote(deposits[(n,unit)])
for n,unit in swaps.keys():
    swaps[(n,unit)] = SimpleQuote(swaps[(n,unit)])

# build rate helpers

dayCounter = Actual360()
settlementDays = 2
depositHelpers = [ DepositRateHelper(QuoteHandle(deposits[(n,unit)]),
                                     Period(n,unit), settlementDays,
                                     calendar, ModifiedFollowing,
                                     False, dayCounter)
                   for n, unit in [(1,Weeks),(1,Months),(3,Months),
                                   (6,Months),(9,Months),(1,Years)] ]

fixedLegFrequency = Annual
fixedLegTenor = Period(1,Years)
fixedLegAdjustment = Unadjusted
fixedLegDayCounter = Thirty360()
floatingLegFrequency = Semiannual
floatingLegTenor = Period(6,Months)
floatingLegAdjustment = ModifiedFollowing
swapHelpers = [ SwapRateHelper(QuoteHandle(swaps[(n,unit)]),
                               Period(n,unit), calendar,
                               fixedLegFrequency, fixedLegAdjustment,
                               fixedLegDayCounter, Euribor6M())
                for n, unit in swaps.keys() ]

# term structure handles

discountTermStructure = RelinkableYieldTermStructureHandle()
forecastTermStructure = RelinkableYieldTermStructureHandle()

# term-structure construction

helpers = depositHelpers + swapHelpers
depoSwapCurve = PiecewiseFlatForward(settlementDate, helpers, Actual360())

ref_date = depoSwapCurve.referenceDate()
yc_day_count = depoSwapCurve.dayCounter()
tenor = Period(10, Years)

# 10Y Swap 

nominal = 1000000
maturity = calendar.advance(settlementDate,10,Years)

fixedRate = 0.04

floatingLegFrequency = Semiannual
spread = 0.0
fixingDays = 2
index = Euribor6M(forecastTermStructure)
floatingLegDayCounter = index.dayCounter()

fixedSchedule = Schedule(settlementDate, maturity,
                         fixedLegTenor, calendar,
                         fixedLegAdjustment, fixedLegAdjustment,
                         DateGeneration.Forward, False)
floatingSchedule = Schedule(settlementDate, maturity,
                            floatingLegTenor, calendar,
                            floatingLegAdjustment, floatingLegAdjustment,
                            DateGeneration.Forward, False)

swap = VanillaSwap(VanillaSwap.Receiver, nominal,
                   fixedSchedule, fixedRate, fixedLegDayCounter,
                   floatingSchedule, index, spread,
                   floatingLegDayCounter)
swap.setPricingEngine(swapEngine)

discountTermStructure.linkTo(depoSwapCurve)
forecastTermStructure.linkTo(depoSwapCurve)

print('original instrument par rate:')
print(swaps[(10, Years)].value())
print
print('calculated swap par rate:')
print(swap.fairRate())
print
print('calculated yield curve par rate:')
print(depoSwapCurve.forwardRate(ref_date, calendar.advance(ref_date, tenor), yc_day_count, Compounded ).rate())
```

From the code above we can see that `swap.fairRate()` returns the par rate of the swap which is very similar to the original swap rate used to construct the yield curve object. This is what I want. However, this method of calculating swap par rate is quite cumbersome, because I want to calculate swap par rate not only for 10Y, but also for 11Y, 12Y, 13Y etc... This means that I'll have to create a separate VanillaSwap object for 11Y, 12Y, 13Y etc...

My second way to calculate swap par rate is `depoSwapCurve.forwardRate(...)`. However, from the code above we can see that the value returned by the `forwardRate(...)` function is quite different from the original swap rate used to construct the yield curve object.

Am I using the `forwardRate(...)` function correctly to calculate par rate? Or is there a better way to calculate par rate from a yield curve object?

## Answer by Luigi Ballabio (score 5, accepted)

https://quant.stackexchange.com/a/37252

No, you'll have to create different swaps. `forwardRate(start, end)` returns the rate from start to end without paying coupons in between (the `Compounding` convention means that the annual interest is reinvested, not paid off).

To make the process less cumbersome, I suggest you define a function `parRate` that takes the starlt and end dates, creates the corresponding swap, and returns its fair rate. Once you have it, calling it should be only slightly more complex than calling `forwardRate`.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.