QuantLib OIS Helpers and the End-of-Month Rule
Summary
The note explains why a QuantLib overnight-index swap helper can generate a one-month curve pillar on October 31 when the valuation date is September 30, even though the overnight index reports its end-of-month flag as false. The cause is the OISRateHelper convention: when the swap starts at month-end, the helper assumes end-of-month scheduling unless told otherwise.
The suggested fix is to set the helper’s end-of-month argument to false explicitly. This is a targeted convention adjustment for matching pillar dates and may help explain valuation differences between implementations. The note also mentions that the Hong Kong Exchange calendar may omit some holidays, which can be added manually; it does not investigate that calendar issue further or show broader valuation comparisons.
Key ideas
- An OISRateHelper can apply an end-of-month convention independently of the overnight index setting.
- A swap starting on month-end may therefore produce a later pillar date than expected.
- Set the helper’s end-of-month option to false when that convention is not wanted.
- The note also flags possible missing Hong Kong Exchange holidays but gives no analysis of them.
Tags
Full text
# Quantlib endOfMonth rule for ql.OvernightIndex
# Quantlib endOfMonth rule for ql.OvernightIndex
I was investigating a valuation difference I was getting from my Quantlib based Python script and a commercially available valuation tool for the HKD HONIA curve. It appears to be because the pillar dates generated are different in some cases. See code below.
```
import QuantLib as ql
# HONIA
qlValuationDate = ql.Date(30, 9, 2024)
ql.Settings.instance().evaluationDate = qlValuationDate
ptSettlementDays = 0
crvFixingDays = 0
qlCurrency = ql.HKDCurrency()
qlCalendar = ql.HongKong(ql.HongKong.HKEx)
crvDayCount = ql.Actual365Fixed()
crvPeriodIndex = ql.OvernightIndex('HONIA', crvFixingDays, qlCurrency, qlCalendar, crvDayCount)
helpers = []
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(1,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(3.448/100))), crvPeriodIndex)]
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(2,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(3.396/100))), crvPeriodIndex)]
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(3,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(3.47/100))), crvPeriodIndex)]
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(4,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(3.307/100))), crvPeriodIndex)]
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(5,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(3.217/100))), crvPeriodIndex)]
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(6,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(3.275/100))), crvPeriodIndex)]
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(9,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(2.75/100))), crvPeriodIndex)]
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(1,ql.Years), ql.QuoteHandle(ql.SimpleQuote(float(2.56/100))), crvPeriodIndex)]
marketdataCurve = ql.PiecewiseSplineCubicDiscount(ptSettlementDays, qlCalendar, helpers, crvDayCount)
print(marketdataCurve.nodes())
```
With the ValuationDate set at the 30-Sep-2024 the 1M rate I would expect to have a node date of 30-Oct-2024, but it is coming back at 31-Oct-2024, as if the endOfMonth rule has been set to true, yet crvPeriodIndex.endOfMonth() returns False.
I feel I'm missing something quite simple.
On another note I noticed that the Hong Kong Stock Exchange calendar in ql doesnt have the holidays for end of Jan 2025 as listed on https://www.cmschina.com.hk/en/CS/Calendar, but I think I can add these manually using 'addHoliday'.
## Answer by Luigi Ballabio (score 3)
https://quant.stackexchange.com/a/81607
Unless otherwise specified, the OIS helper assumes that a swap starting on the end of the month has `endOfMonth` set to true. I guess this was the default on some markets, and ended up codified in the class.
To prevent this, you can pass the additional argument `endOfMonth=False` to the constructor of `OISRateHelper`, as in:
```
helpers += [ql.OISRateHelper(ptSettlementDays, ql.Period(1,ql.Months), ql.QuoteHandle(ql.SimpleQuote(float(3.448/100))), crvPeriodIndex, endOfMonth=False)]
```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.