Skip to content

feat(itofin-py): expose moving cap/floor surfaces and extrapolation - #631

Merged
benbenbang merged 4 commits into
mainfrom
fable/623
Jul 27, 2026
Merged

feat(itofin-py): expose moving cap/floor surfaces and extrapolation#631
benbenbang merged 4 commits into
mainfrom
fable/623

Conversation

@benbenbang

@benbenbang benbenbang commented Jul 27, 2026

Copy link
Copy Markdown
Owner
  • feat(itofin-py): expose moving cap/floor surfaces and extrapolation
  • feat(itofin-py): expose optionlet stripper and adapter facades
  • test(itofin-py): add optionlet stripping tests
  • test(capfloor): cover moving surfaces and optionlet frequency override

close #623

This adds the floating-reference-date constructors and extrapolation
controls needed by the optionlet stripping pipeline.

Partially discharges #627: the `capfloortermvol.rs` /
`CapFloorTermVolSurface` site listed there is done here. It was not
optional polish - without it the #623 pass is unbuildable, since the
adapter reads its settlement days back off the term-vol surface. What
remains under #627 is the two `ConstantSwaptionVolatility` and two
`ConstantOptionletVolatility` moving forms.

**Moving CapFloorTermVolSurface constructors:**
* Added `moving` and `moving_with_quotes` static methods to
  `CapFloorTermVolSurface`, whose reference date floats `settlement_days`
  off the evaluation date, over fixed volatilities or the caller's quotes.
* These carry the settlement days `StrippedOptionletAdapter` reads back
  off the stripper; a fixed-reference surface has none and fails the
  adapter with "settlement days not provided for this instance"
  (`strippedoptionletadapter.rs:87` -> `optionletstripper.rs:241` ->
  `termstructures/mod.rs:227`).
* Updated the type stubs and module docs to reflect that all four core
  constructors are now exposed; the stale "not exposed" claims are gone
  rather than re-pointed.

**Optionlet volatility extrapolation:**
* Added `allows_extrapolation`, `enable_extrapolation`, and
  `disable_extrapolation` to the optionlet volatility surface, since a
  stripped surface ends at its last optionlet fixing and a cap whose own
  last caplet fixes there queries the boundary.

**Docs:**
* The deferred moving constant optionlet constructors keep their
  deferral and now cite #627.
This pull request adds Python bindings for the cap/floor optionlet
stripping stack, closing the volatility loop from market cap term
volatilities to a caplet surface.

**New optionlet stripping facades:**
* Added `PyOptionletStripper1` that bootstraps caplet volatilities out of a `CapFloorTermVolSurface` and an `Euribor` index, exposing `switch_strike` and `atm_optionlet_rates` queries. Stripping is lazy and cached, and the `Normal` volatility type fails at the strip.
* Added `PyStrippedOptionletAdapter`, extending `PyOptionletVolatilityStructure`, which serves the stripper's caplet grid as an interpolated volatility surface so a `BlackCapFloorEngine` reprices the quoted caps.
* Registered both classes in the `termstructures` module and dropped the now-used `dead_code` allow on `PyCapFloorTermVolSurface::inner`.

**Type stubs:**
* Added `OptionletStripper1` and `StrippedOptionletAdapter` definitions to `termstructures.pyi`, documenting the moving-surface requirement, discount and frequency fallbacks, and the extrapolation caveat.
Add a new test file `crates/itofin-py/tests/test_optionlet_stripping.py`
to validate optionlet stripping behavior.

Closes #623
Extend the cap/floor term vol tests to exercise the two moving
constructors alongside the pinned ones, so a transposed grid cannot
hide behind the flat fixture the stripping oracle relies on.

**Cap/floor term vol surface**:
* Add `_moving_matrix_surface` and `_moving_quote_surface` fixtures at
  zero settlement days, pinning their reference date to the evaluation
  date via a shared `SETTINGS` on `REFERENCE`.
* Parametrize node recovery over all four constructors and the quote
  bump test over both quote forms.

**Optionlet stripping**:
* Thread an `optionlet_frequency` argument through `_stripper` and add a
  test proving a 1Y override on a 6M index halves the caplet count,
  confirming the argument reaches the core rather than being dropped by
  the facade.
* Strengthen the anti-tautology guard assertion message to explain why a
  non-zero worst round-trip error proves the stripped vols differ from
  the flat input.

close #623
@benbenbang
benbenbang enabled auto-merge (rebase) July 27, 2026 05:27
@github-actions github-actions Bot added the enhancement New feature or request label Jul 27, 2026
@benbenbang
benbenbang merged commit 41af653 into main Jul 27, 2026
3 of 6 checks passed
@benbenbang
benbenbang deleted the fable/623 branch July 27, 2026 05:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[D7][itofin-py] Pass B3: PyOptionletStripper1 + PyStrippedOptionletAdapter (#605)

1 participant