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_IMMconvention resolves its tenors by rolling whole IMM quarters from the anchor, and no resolver path throws for it. - The resolution rows for
CREDIT_CDS_IMMare 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
11.4. See also
- ores.marketdata.tenor_convention — where
resolution_algorithmis modeled. - ores.marketdata.tenor_convention_resolution — where the CDS resolution rows would be seeded.