Skip to content
All library documents

How QuantLib Sets Floating Coupon Frequency in Zero-Coupon Swaps

Article Quant Q&A · Author: John83

Summary

The document explains how to determine the floating coupon schedule and rate calculation for a QuantLib zero-coupon swap. It states that the index tenor controls the spacing of floating subperiods, and demonstrates inspecting a floating coupon’s dates, accrual fractions, fixings, rate, and amount. The example constructs a one-year Ibor index and a zero-coupon swap, then compares its floating leg with a standalone subperiod coupon.

The floating rate is obtained by compounding the subperiod index fixings and converting the resulting factor into a rate over the full accrual period. The example shows that this calculation reproduces the swap coupon amount, and suggests using coupon value dates to inspect payment periods. The examples use a Swedish index and curve, so they illustrate the mechanics rather than provide BRL market conventions or resolve the question’s large valuation difference. Correct currency, calendar, curve, and index setup still matter for a Brazilian contract.

Key ideas

  • The index tenor determines the spacing of floating subperiods in the illustrated zero-coupon swap.
  • The floating coupon compounds index fixings over subperiods across the full accrual period.
  • Coupon attributes can be inspected to examine fixing, accrual, payment, rate, and amount details.
  • A standalone subperiod coupon can be used to reproduce and understand the swap’s floating amount.
  • The sample setup uses Swedish conventions and does not establish appropriate BRL market inputs.

Tags

Full text
# QuantLib: Pricing BRL zero coupon swap using relevant attributes in Quantlib


# QuantLib: Pricing BRL zero coupon swap using relevant attributes in Quantlib












I am trying to price the BRL zero coupon swap. As we know that ZC swaps fixed payer pays a single payment at maturity and the float payer pays the interim payments till maturity. So in this case, where is the payment frequency or schedule attribute for pricing this swap? If I follow the below-mentioned way, I am getting a huge variation in swap.NPV compared to the given NPV. Please guide me if I am following the right approach.

```
maturity = []
discountfactor = []
day_count = ql.Actual360()
 calendar = ql.JointCalendar(ql.UnitedStates(), ql.UnitedKingdom())
  yield_curve = ql.DiscountCurve(maturity, discountfactor, day_count, calendar)
 yield_curve.enableExtrapolation()
 curve_handle = ql.YieldTermStructureHandle(yield_curve)

 # BRL index
  BRL_index = ql.OvernightIndex('cdi', 0, ql.BRLCurrency(), ql.Brazil(), ql.Business252())
  notional = 20000000
  notional currency = 'BRL'
  fixed_rate = 0.05
  fixed_leg_daycount = ql.Business252()
  float_spread = 0.0
  float_leg_daycount = ql.Business252()
  payment frequency = ql.Once

 #applying zero coupon swap function
 swap = ql.ZeroCouponSwap(ql.Swap.Receiver, notional, start_date, end_date,
                     fixed_rate, fixed_leg_daycount, BRL_index, ql.Brazil())

 engine = ql.DiscountingSwapEngine(yield_curve)
 swap.setPricingEngine(engine)
  npv = swap.NPV
```

## Answer by Xiarpedia (score 1, accepted)

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

"The short answer is that the frequency of the floating payments is determined by the `Schedule` of an `IborIndex` (or `OvernightIndex`). Thus, if we define the following

```
ibor_index= ql.IborIndex(
    "MyIndex",
    ql.Period("1Y"),
    0,
    ql.SEKCurrency(),
    ql.Sweden(),
    ql.ModifiedFollowing,
    False,
    ql.Actual360(),
    ts_handle,
)
```

It can be interpreted as the coupons of the `ZeroCouponSwap` will have a frequency of 1Y, i.e. each coupon payment day will occur 1Y between.

If we consider an more in-depth answer. Then I found the definition of ZeroCouponSwap to be very helpful when researching this particular class. Furthermore, assume that we want to replicate the following amount of this particular `ZeroCouponSwap` to fully grasp how the floating leg of `ZeroCouponSwap` is priced:

```
import QuantLib as ql
import pandas as pd
# Set the reference date
ref_date = ql.Date(1, 1, 2022)
ql.Settings.instance().evaluationDate = ql.Date(1, 1, 2022)

dfs = [1, 0.965, 0.94]  # discount factors
dates = [
    ql.Date(1, 1, 2022),
    ql.Date(1, 1, 2023),
    ql.Date(1, 1, 2025),
]  # maturity dates of the discount factors

day_counter = ql.Actual360()

# Create the discount curve
curve = ql.DiscountCurve(dates, dfs, day_counter)

# The curve will note be linked in case we want to update the quotes later on
ts_handle = ql.YieldTermStructureHandle(curve)

custom_index= ql.IborIndex(
    "MyIndex",
    ql.Period("1Y"),
    0,
    ql.SEKCurrency(),
    ql.Sweden(),
    ql.ModifiedFollowing,
    False,
    ql.Actual360(),
    ts_handle,
)

start_date= ql.Date(1,1,2022)
end_date = ql.Date(1,1,2024)
notional= 10e6
swap = ql.ZeroCouponSwap(
    ql.Swap.Payer,
    notional,
    start_date,
    end_date,
    0.02,
    day_counter,
    custom_index,
    ql.Sweden(),
)

engine = ql.DiscountingSwapEngine(ts_handle)
swap.setPricingEngine(engine)

pd.DataFrame([{
    'fixingDate': cf.fixingDate().ISO(),
    'accrualStart': cf.accrualStartDate().ISO(),
    'accrualEnd': cf.accrualEndDate().ISO(),
    'accrualPeriod': cf.accrualPeriod(),
    "paymentDate": cf.date().ISO(),
    'forward': cf.indexFixing(),
    'rate': cf.rate(),
    "amount": cf.amount(),
} for cf in map(ql.as_floating_rate_coupon, swap.leg(1))])
```

The code above will result in the following values:

| fixingDate | accrualStart | accrualEnd | accrualPeriod | paymentDate | forward | rate | amount |
| 2023-06-05 | 2022-02-03 | 2024-06-03 | 2.363888889 | 2024-06-03 | 0.0130119 | 0.021911 | 517947.6 |

Then in reference to the aforementioned link the floating rate of the coupons for `ZeroCouponSwap` are calculated by:

$$R^{FLT} = \left[ \prod_{k=0}^{K-1} (1+\alpha(T_{k},T_{k+1}) L(T_{k},T_{k+1})) -1 \right]$$

Thus, we aim to identify the attributes that QuantLib utilizes to calculate the floating rate amount. However, upon inspecting the source code of QuantLib, one will observe that the floating leg of the ZeroCouponSwap comprises simply of SubPeriodsCoupon. To simplify this process, we can use the following code to determine the exact attributes that are employed (as I have understood the floating coupon payments can not be found in the original `ZeroCouponSwap` class):

```
import QuantLib as ql
import pandas as pd
# Set the reference date
ql.Settings.instance().evaluationDate = ql.Date(1, 1, 2022)

dfs = [1, 0.965, 0.94]  # discount factors
dates = [
    ql.Date(1, 1, 2022),
    ql.Date(1, 1, 2023),
    ql.Date(1, 1, 2025),
]  # maturity dates of the discount factors

day_counter = ql.Actual360()

# Create the discount curve
curve = ql.DiscountCurve(dates, dfs, day_counter)

# The curve will note be linked in case we want to update the quotes later on
ts_handle = ql.YieldTermStructureHandle(curve)

custom_index= ql.IborIndex(
    "MyIndex",
    ql.Period("1Y"),
    0,
    ql.SEKCurrency(),
    ql.Sweden(),
    ql.ModifiedFollowing,
    False,
    ql.Actual360(),
    ts_handle,
)

start_date= ql.Date(1,1,2022)
end_date = ql.Date(1,1,2024)

coupon = ql.SubPeriodsCoupon(ql.Date(3,6,2024), 1e7, start_date, end_date, 0, custom_index)

coupon.setPricer(ql.CompoundingRatePricer())
print(f'{coupon.amount() = }')
```

This will give us the identical amount as `ZeroCouponSwap`. Thus, given the equation that was found in the documentation we arrive at

```
coupon_fixing_dates = coupon.fixingDates()
sub_period_fixings = []
for fixing_date in coupon_fixing_dates:
    sub_period_fixings.append(custom_index.fixing(fixing_date))
sub_period_fractions = coupon.dt()
compound_factor = 1 
for fixing, frac in zip(sub_period_fixings, sub_period_fractions):
    compound_factor *= 1+fixing * frac 
rate = ((compound_factor-1) / coupon.accrualPeriod())
```

Which produces the number 0.021910825388354933. Which is the number that is used to produce the amount of the floating leg. Using this value multiplied by $10^6$ and `accrualPeriod` we arrive at the exact amount.

I expect the most efficient way to view coupon payment days is to use `coupon.valueDates()` which is created with `ql.MakeSchedule` using the `start_date`, `end_date` and `ql.Period` to see the cashflows of the floating leg.

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.