Task: Implement IMM_ROLL tenor resolution for credit/CDS conventions

Table of Contents

This page documents a task in the Close the credit loose ends story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

Implement the IMM_ROLL branch of the tenor resolver (ores.marketdata.api::domain::resolve_end_date()), which currently throws std::logic_error whenever tenor_convention.resolution_algorithm is IMM_ROLL. This means stepping through IMM Dates' quarterly roll schedule by offset_multiplier roll-quarters from the horizon's most recent roll, rather than the anchor+offset arithmetic the ANCHOR_OFFSET branch already implements. Also seed the tenor_convention_resolution rows for the CREDIT_CDS_IMM convention (offset_unit = 'ROLL_QUARTER', offset_multiplier = the roll-quarter count per nominal tenor, e.g. 1Y 1RQ) — deliberately not done in the originating task since the exact roll-quarter-per-tenor mapping was not sourced anywhere in the knowledge cluster and fabricating it would have been a hack.

2. Status

Field Value
State DONE
Parent story Close the credit loose ends
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-10-15

3. Acceptance

  • A CREDIT_CDS_IMM convention resolves its tenors by rolling whole IMM quarters from the anchor, and no resolver path throws for it.
  • The resolution rows for CREDIT_CDS_IMM are seeded, not hand-written per tenant.
  • A test pins the resolved date, so a change to the roll rule fails the suite.

4. Plan

(Implementation strategy. Written when work starts; key decisions are distilled into the parent story's * Decisions at close, but the plan itself stays — it is the historical record of what we did.)

5. Measurements

Every number this task claims about a tree, with the command that produced it and the commit it was measured at. A number without both is not a measurement, because a clean-up record describes a tree and trunk moves under a branch: a consumer count, a gate's component count and a suite's size all change with no code changing, and the reader of a bare number cannot tell which tree it came from. Where the raw output matters, put it in an evidence/ file beside the task and name that file in the Command column.

What Value Command Commit
       

6. Notes

7. Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

8. PRs

PR Title
#3390 [agile] Close the IMM roll task as already delivered

9. Review

Comment summary File Decision Notes
       

10. Result

Already delivered. Nothing was implemented for this task, because main already does it and the capture it came from had gone stale.

The capture was right when it was written: the resolver threw for an IMM_ROLL convention. The unified tenor resolution work then superseded that design. The algorithm vocabulary is now (ANCHOR_OFFSET, SCHEDULE_STEP), the old IMM_ROLL algorithm row is gone, and IMM_ROLL survives only as a measured_from anchor. CREDIT_CDS_IMM is seeded as a SCHEDULE_STEP row whose schedule is ROLL_QUARTER, in refdata_tenor_convention_resolutions_populate.sql.

The resolver computes the roll closed-form in projects/ores.refdata/api/src/domain/tenor_resolution.cpp: roll_quarter_date() returns the 20th of the March/June/September/December quarter, and walk_roll_quarter() counts the requested quarters forward from the anchor.

That the rule is the 20th, and not a business-day adjustment of it, is a correction this task made. The knowledge page had called the 20th "the first business day following" it, and the code advanced a weekend date to the next weekday. Both were wrong, and neither matched a market convention. There are two conventions under the name "IMM date", and the page now documents both: the true IMM date is the third Wednesday of the quarter month, used by exchange-traded STIR futures, and the CDS roll date is the 20th, used by credit default swaps. ROLL_QUARTER models the second, because its only consumer is CREDIT_CDS_IMM. The 20th is kept exactly, weekend or not, since that is what lets every contract in a CDS series mature on one date. The third-Wednesday IMM date has no schedule and is recorded as a gap.

Two tests pin it: resolve_end_date_migrated_credit_cds_imm_resolves_to_pinned_roll_quarter and resolve_end_date_walks_roll_quarter_from_anchor_with_zero_offset, both in domain_tenor_resolution_tests.cpp. They pass, and ores.refdata.api.tests passed in the full gate.

Acceptance met as written above, by the earlier work. No code changed here.

11. Promoted from capture

Captured 2026-07-13 in the product backlog; promoted preserving the UUID.

11.1. What

Implement the IMM_ROLL branch of the tenor resolver (ores.marketdata.api::domain::resolve_end_date()), which currently throws std::logic_error whenever tenor_convention.resolution_algorithm is IMM_ROLL. This means stepping through IMM Dates' quarterly roll schedule by offset_multiplier roll-quarters from the horizon's most recent roll, rather than the anchor+offset arithmetic the ANCHOR_OFFSET branch already implements. Also seed the tenor_convention_resolution rows for the CREDIT_CDS_IMM convention (offset_unit = 'ROLL_QUARTER', offset_multiplier = the roll-quarter count per nominal tenor, e.g. 1Y 1RQ) — deliberately not done in the originating task since the exact roll-quarter-per-tenor mapping was not sourced anywhere in the knowledge cluster and fabricating it would have been a hack.

11.2. Why

The persisted tenor data model (tenor=/=tenor_anchor=/ =tenor_convention=/=tenor_convention_resolution) was deliberately designed so credit/CDS's genuinely different resolution algorithm fits without a schema change: resolution_algorithm and the offset_unit=/=offset_multiplier columns already accommodate it. Only the resolver code and CDS-specific seed data are outstanding — this capture exists so that gap doesn't get silently forgotten once the ["DQ dataset for tenor reference data"] work closes as apparently complete for two conventions.

11.3. References

  • Tenor — the "Tenor conventions by curve type" section, including the CDS 1Y 1RQ disambiguation notation.
  • IMM Dates — the quarterly roll schedule to step through.

11.4. See also

Emacs 29.3 (Org mode 9.6.15)