Task: Dual-curve (discount + projection) short-rate model

Table of Contents

This page documents a task in the IR Rates synthetic data: dataset seeding, index cleanup, dual-curve, quoting conventions story. It captures the goal, current status, acceptance, and any notes or results.

Goal

Today ir_curve_generation_config drives exactly one short-rate process, whose discount_factor() feeds every instrument (deposits, FRAs, swaps) via curve_instrument_pricer – a single self-discounting curve, deliberately simplified for Phase 1 (confirmed intentional for the current PoC, not a gap). Real post-2008 market practice separates OIS discounting from index/IBOR projection, with a basis spread between them. This task adds that: a second process/curve per currency+index representing the discounting leg, distinct from the projection/index leg the existing process already models, with a basis spread parameter between them.

Scope

  • curve_instrument_pricer needs to accept two discount-factor sources (discounting + projection) instead of one.
  • New config fields for the discounting curve's own process parameters (mirroring the existing projection process' kappa/theta/ sigma/initial_rate).
  • Separate charts/UI in the IR curve editor to show both curves distinctly (not one merged "curve shape" chart).
  • Raised while reviewing the IR curve editor's charts in Tick-batch publishing and persistence for curve instruments; deliberately deferred out of that task's scope.

Note: not a mismatch today

The seeded indices (SOFR/US, ESTR/EUR, SONIA/GBP) are all overnight RFRs – the same logic applies uniformly to all three, none is a special case. An OIS swap referencing an overnight index genuinely is a single-curve, self-discounting instrument in real markets: its floating leg settles at the overnight rate itself, and that same overnight rate is also the standard discounting rate for cleared/ collateralized derivatives in that currency (this is exactly what the post-2008 multi-curve framework's "OIS-discounting" leg is – the OIS curve itself, built from OIS instruments, is single-curve by construction; it is only the IBOR-projection leg layered on top of it that needs a second curve). So single-curve modeling is the correct choice for what is currently seeded, not a simplification that happens to work. The mismatch this task actually resolves would arise from adding an IBOR-style term index (EURIBOR, legacy LIBOR, term SOFR) – those carry a credit/liquidity/term premium distinct from the risk-free overnight rate, so a swap referencing one genuinely needs OIS discounting plus a separate IBOR projection curve. IBOR-style indices should not be added to the seeded universe until this task lands.

Status

Field Value
State BACKLOG
Parent story IR Rates synthetic data: dataset seeding, index cleanup, dual-curve, quoting conventions
Now Not yet started.
Waiting on Nothing.
Next Begin implementation.
Last touched 2026-07-19

Acceptance

  • A decision is made and documented on the config-model shape (second process embedded in ir_curve_generation_config, vs. a separate linked discounting-curve entity).
  • curve_instrument_pricer derives each instrument's rate from the correct pair of discount factors (discounting curve for the numeraire, projection curve for the index leg) rather than one shared source.
  • The IR curve editor shows both curves distinctly (separate charts or a clearly-labelled combined view, not a silent merge).
  • Existing single-curve configs continue to work (either migrated to a degenerate discounting==projection case, or an explicit toggle).

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.)

Notes

Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario, run through the QA Validation Runner panel) 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
     

PRs

PR Title
   

Review

Comment summary File Decision Notes
       

Result

Emacs 29.3 (Org mode 9.6.15)