lballabio / QuantLib

The QuantLib C++ library
http://quantlib.org
Other
5.21k stars 1.78k forks source link

Extensions to OvernightIndexedCoupon and OvernightLeg for FRN #1422

Closed klin333 closed 2 weeks ago

klin333 commented 2 years ago

Hi,

OvernightIndexedCoupon and OvernightLeg may need to be revamped to handle the different ways overnight indexed floating rate bonds work. The variously ways are detailed in https://www.newyorkfed.org/medialibrary/Microsites/arrc/files/2021/users-guide-to-sofr2021-update.pdf see page 16-19. I summarise below, with potential ways to implement in QuantLib.

Lookback with observation shift

This appears by far the most common in USD SOFR floating bonds, almost always with 2 business day observation shift, eg US961214EV12. You can tell in Bloomberg via the field OBSRVTN_PERD_ADJSTD_DAYS.

In this convention, the daily schedule (not the quarterly coupon payment schedule) used to compute daily compounded SOFR rates is shifted back 2 days, see diagram in page 19 of above linked guide. Note that this 'observation shift' is different to the 'observation lag' used in QuantLib CPI coupons, the difference being whether the schedule, used for calculating the number of calendar days each rate applies, is also shifted.

One way to handle this is modify OvernightLeg, to add an 'observationShift' parameter that shifts the coupon accrual period, see below. I omitted boiler plate changes for demonstration.

OvernightLeg::operator Leg() const {

        ... unchanged code... 

        Size n = schedule_.size()-1;
        for (Size i=0; i<n; ++i) {
            if (observationShift_ == 0) {
                refStart = start = schedule_.date(i);
                refEnd   =   end = schedule_.date(i+1);
            } else {
                // can consider collapse into the 0 case
                refStart = start = overnightIndex_->fixingCalendar().advance(schedule_.date(i), -observationShift_, Days, Unadjusted);
                refEnd   =   end = overnightIndex_->fixingCalendar().advance(schedule_.date(i+1), -observationShift_, Days, Unadjusted);
            }
            paymentDate = paymentCalendar_.advance(schedule_.date(i+1), paymentLag_, Days, paymentAdjustment_);

            ... unchanged code...  
            // start and end passed to  OvernightIndexedCoupon()
        return cashflows;
    }

Payment Delay (usually with a lockout for the final period)

This appears the next common convention in SOFR bonds, eg Bloomberg security ZQ993367 Corp. This is already handled in QuantLib via paymentLag

Usually FRNs with payment lag comes with a lockout period for the very last coupon, to ensure that final payment occurs exactly on bond contractual maturity date. Potentially this can be also implemented in OvernightLeg::operator Leg(), eg

if (lockupLastPayment_ && i+1 == schedule.size()-1) {
  paymentDate = schedule_.date(i+1);
} else {
  paymentDate = paymentCalendar_.advance(schedule_.date(i+1), paymentLag_, Days, paymentAdjustment_);
}

As for the current date interest accrued as of some evaluation date, i'm not sure about this. I believe what Bloomberg does is, given a bond settlement date, assuming 2 day payment delay, for interest accrued, only SOFR rates up to and including settle date - 3 business days, is used, with constant forward filling of sofr past that date. This appears like effectively a lockout period type of convention. Perhaps this can be handled by setting a max ceiling on the fixingDates_, eg by modifying this bit of code below https://github.com/lballabio/QuantLib/blob/9325cfc482b794f277a1aa96f2e1a7cf25cab260/ql/cashflows/overnightindexedcoupon.cpp#L203-L213

in the bit of code below

Lookback without observation shift

This appears the convention in Australia (AONIA eg AW365071 Corp) and euro (SONIA eg XS2454288122 and ESTR eg BN646169 Corp). You can tell in Bloomberg via OBSRVTN_PERD_ADJSTD_DAYS = NA, and FLT_DAYS_PRIOR for the lookback day.

This should be very similar to the 'obervation lag' used in QuantLib CPI coupons, though for overnight FRN, it is lag in business days while CPI appears to be lags in calendar periods.

Something similar to the CPI lag method probably can be done, eg by changing this bit of code https://github.com/lballabio/QuantLib/blob/9325cfc482b794f277a1aa96f2e1a7cf25cab260/ql/cashflows/overnightindexedcoupon.cpp#L203-L213

github-actions[bot] commented 2 years ago

This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.

lballabio commented 2 weeks ago

Fixed by https://github.com/lballabio/QuantLib/pull/1985 in version 1.35.