Skip to content

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

brianweller89
Copy link
Contributor

@brianweller89 brianweller89 commented May 1, 2025

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 to QUARTERLY_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

  • May contract accrual period: May to Aug
  • June contract accrual period: June to Sep
  • July contract accrual period: July to Oct

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

  1. The length of the accrual period
    • This has been added to OvernightFutureContractSpec, as it's a property of the contract not the date sequence
    • This can then be added to the start date
  2. The adjustment to be applied in order to ensure the date is valid according to the rules for that specific future (e.g. a valid IMM date)
    • This is done using the dateMatching method of DateSequence
    • This is a bit of a hack as the end date calculation is not really a concern of the DateSequence; but currently the DateSequence 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

* <p>
* This is the period of time over which the daily interest rate fixing is observed.
*/
@PropertyDefinition(get = "optional")
Copy link
Contributor Author

@brianweller89 brianweller89 May 1, 2025

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);
Copy link
Contributor Author

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

@brianweller89 brianweller89 marked this pull request as draft May 1, 2025 14:28
@brianweller89
Copy link
Contributor Author

brianweller89 commented May 1, 2025

@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));
Copy link
Contributor Author

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

Copy link
Member

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)

Copy link
Member

@jodastephen jodastephen left a 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))
Copy link
Member

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));
Copy link
Member

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants