-
Notifications
You must be signed in to change notification settings - Fork 294
Fix overnight future contract spec #2719
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
* <p> | ||
* This is the period of time over which the daily interest rate fixing is observed. | ||
*/ | ||
@PropertyDefinition(get = "optional") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer to make this non-optional - but that would make this class non backwards compatible. The class is used in a couple of internal repos, but we could easily update the usages to add the Period
YearMonth finalReferenceMonth = YearMonth.from(referenceDate.plus(getAccrualPeriod().get())); | ||
return dateSequence.dateMatching(finalReferenceMonth); | ||
} else { | ||
return dateSequence.baseSequence().next(referenceDate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If accrualPeriod is not optional then can remove this fallback
@jodastephen Can I get your view on this change please? If we're happy with the approach then I need to populate the period on more contract specs, but have just done the 1 for now so we can agree the approach |
assertThat(trade.getProduct().getAccrualMethod()).isEqualTo(COMPOUNDED); | ||
assertThat(trade.getProduct().getAccrualFactor()).isEqualTo(3 / 12d); | ||
assertThat(trade.getProduct().getStartDate()).isEqualTo(date(2025, 7, 16)); | ||
assertThat(trade.getProduct().getEndDate()).isEqualTo(date(2025, 10, 14)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the key thing that is changing. Prior to the change this test would have incorrectly returned a September date as the end date for the July future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this PR cover all case?
Is the start date always interpretted as being at the start of the date sequence, or can it be part-way through? (ie. I think this PR works because 3 months < 6 serial, but will break in more complex cases)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My view is that this PR works for a subset of use cases but I suspect it doens't work for all.
.accrualMethod(COMPOUNDED) | ||
.accrualPeriod(Period.ofMonths(3)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be populated for all constants in StandardOvernightFutureContractSpecs
for consistency
assertThat(trade.getProduct().getAccrualMethod()).isEqualTo(COMPOUNDED); | ||
assertThat(trade.getProduct().getAccrualFactor()).isEqualTo(3 / 12d); | ||
assertThat(trade.getProduct().getStartDate()).isEqualTo(date(2025, 7, 16)); | ||
assertThat(trade.getProduct().getEndDate()).isEqualTo(date(2025, 10, 14)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this PR cover all case?
Is the start date always interpretted as being at the start of the date sequence, or can it be part-way through? (ie. I think this PR works because 3 months < 6 serial, but will break in more complex cases)
We currently rely on the
DateSequence
to determine the end date for overnight futures. We assume that the next 'sequence date' after the start date is the correct end date.This works fine in the case where we have futures with no overlapping observation periods. For example, the SOFR 1M future is issued every month and each contract has a month long accrual period. Therefore the start date of each SOFR 1M future is the day immediately after the end date of the previous future; there is no overlap.
However for SOFR 3M we have a problem. Currently, we model this future using the
QUARTERLY
date sequence, which means that we only expect futures to be issued in Mar, Jun, Sep and Dec. This is incorrect; we can see here that in addition to quarterly futures going out for ~10 years, there are also monthly contracts available for each of the next 6 months.Therefore, to get the
DateSequence
correct we need to change the date sequence toQUARTERLY_IMM_6_SERIAL
, as I've done in this PR.This should create 'overlapping' periods, because the accrual period of the contract (3M) is not aligned with the frequency of the contract issuance (monthly for the first 6 months). For example
This causes our logic for computing the end date to break, as we use the 'base sequence' (in this case Quarterly IMM) to compute the end date. Hence we always select an end date that is in one of the four IMM quarters; this behaviour is only correct when the contract month itself is also the IMM month.
In order to generate a correct end date we need two pieces of information
OvernightFutureContractSpec
, as it's a property of the contract not the date sequencedateMatching
method ofDateSequence
DateSequence
; but currently theDateSequence
is the class which knows the correct adjustment rule, and I don't really want to duplicate that adjustment rule onto the contract spec.This change is implicitly changing the meaning/usage of the date sequence. Previously it was assumed to represent a 'non overlapping' sequence of dates which define the accrual periods for a chain of futures. Whereas now it is representing the start date of the accrual periods for the futures chain only, with the end dates being calculated based on additional logic