Skip to content

Reject ParYieldCurve-settled swaptions in the finite-difference swaption engines - #2997

Open
leafyoung wants to merge 2 commits into
lballabio:masterfrom
leafyoung:fix-fd-swaption-engines-settlement-method
Open

leafyoung wants to merge 2 commits into
lballabio:masterfrom
leafyoung:fix-fd-swaption-engines-settlement-method

Conversation

@leafyoung

@leafyoung leafyoung commented Nov 1, 2026 •

Copy link
Copy Markdown

Summary

FdG2SwaptionEngine::calculate() and FdHullWhiteSwaptionEngine::calculate() never checked arguments_.settlementType/settlementMethod at all. Both value a European swaption with FdmAffineModelSwapInnerValue, which replicates the exercise payoff from the underlying swap's own cashflows under the short-rate model — the same physical-style payoff a Physical and a Cash+CollateralizedCashPrice settled swaption share by definition. Without a guard, a Cash+ParYieldCurve swaption — whose payoff genuinely differs (an annuity-based formula this approach doesn't implement) — was silently priced as if it were physically exercised instead of being rejected.

Every other engine valuing a swaption this way already guards with settlementMethod != Settlement::ParYieldCurve:

These two finite-difference engines were simply missing the check entirely — a related but distinct defect from #2996 (that one over-rejected a valid input with a loud error; this one silently mis-prices an unsupported one with no error at all).

Test plan

  • Added testFdSwaptionEnginesCashSettlement in test-suite/bermudanswaption.cpp: for both FdHullWhiteSwaptionEngine and FdG2SwaptionEngine, prices the same European swaption under Settlement::Physical and Settlement::Cash+CollateralizedCashPrice and checks the NPVs match, and confirms Cash+ParYieldCurve is now correctly rejected.
  • Verified the new test fails without the guard ("exception Error expected but not raised" on the ParYieldCurve case, for both engines) and passes with the fix.
  • Ran the full BermudanSwaptionTests suite — all pass.

🤖 Co-worked with Claude Code

…ion engines

FdG2SwaptionEngine::calculate() and FdHullWhiteSwaptionEngine::calculate()
never checked arguments_.settlementType/settlementMethod at all. Both value
a European swaption with FdmAffineModelSwapInnerValue, which replicates the
exercise payoff from the underlying swap's own cashflows under the
short-rate model -- the same physical-style payoff a Physical and a
Cash+CollateralizedCashPrice settled swaption share by definition. Without
a guard, a Cash+ParYieldCurve swaption -- whose payoff genuinely differs
(an annuity-based formula this approach doesn't implement) -- was silently
priced as if it were physically exercised instead of being rejected.

Every other engine valuing a swaption this way already guards with
`settlementMethod != Settlement::ParYieldCurve` (JamshidianSwaptionEngine,
Gaussian1dJamshidianSwaptionEngine, Gaussian1dSwaptionEngine,
Gaussian1dNonstandardSwaptionEngine, Gaussian1dFloatFloatSwaptionEngine,
TreeSwaptionEngine, and now G2SwaptionEngine per lballabio#2996); these two
finite-difference engines were simply missing the check entirely.

Adds testFdSwaptionEnginesCashSettlement in test-suite/bermudanswaption.cpp,
checking both engines price a Cash+CollateralizedCashPrice swaption
identically to the same swaption settled Physically, and that
Cash+ParYieldCurve is now correctly rejected (previously silently priced
with no error).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 1, 2026 06:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The test description incorrectly presents pre-fix behavior as current, although both engines now reject ParYieldCurve rather than silently pricing it as physical settlement.

1 open issue
What changed in this PR

This PR prevents finite-difference swaption engines from silently pricing unsupported ParYieldCurve settlement, aligning them with other short-rate swaption engines.

Changes:

  • Add settlement-method guards to the Hull–White and G2 finite-difference engines.
  • Add regression coverage for physical/collateralized-cash price equivalence and rejection of ParYieldCurve settlement.
File Description
test-suite/​bermudanswaption.cpp Tests supported settlement equivalence and unsupported settlement rejection in both engines.
ql/​pricingengines/​swaption/​fdhullwhiteswaptionengine.cpp Rejects ParYieldCurve settlement before Hull–White valuation.
ql/​pricingengines/​swaption/​fdg2swaptionengine.cpp Rejects ParYieldCurve settlement before G2 valuation.

Review effort: Balanced

Comment thread test-suite/bermudanswaption.cpp Outdated
Comment on lines +336 to +340
// settled swaption share by definition. Neither engine validates
// settlementType/settlementMethod at all, so a Cash+ParYieldCurve
// swaption (whose payoff genuinely differs -- an annuity-based formula
// this approach doesn't implement) is silently priced as if it were
// physically exercised instead of being rejected.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed in 16fc394 — reworded to past tense for the original defect and added a sentence stating what the test verifies now (that Physical/Cash+CollateralizedCashPrice still price identically and Cash+ParYieldCurve is rejected).

The comment described the pre-fix behavior (no settlement validation,
silent physical-style pricing of ParYieldCurve) in the present tense,
as if it were still current. Reworded to past tense for the original
defect and added a sentence describing what the test verifies now.

Co-worked with Claude Code
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