Repository navigation
Conversation
…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>
There was a problem hiding this comment.
🟡 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
ParYieldCurvesettlement.
| 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 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. |
Author
There was a problem hiding this comment.
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
FdG2SwaptionEngine::calculate()andFdHullWhiteSwaptionEngine::calculate()never checkedarguments_.settlementType/settlementMethodat all. Both value a European swaption withFdmAffineModelSwapInnerValue, which replicates the exercise payoff from the underlying swap's own cashflows under the short-rate model — the same physical-style payoff aPhysicaland aCash+CollateralizedCashPricesettled swaption share by definition. Without a guard, aCash+ParYieldCurveswaption — 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:JamshidianSwaptionEngineGaussian1dJamshidianSwaptionEngineGaussian1dSwaptionEngineGaussian1dNonstandardSwaptionEngineGaussian1dFloatFloatSwaptionEngineTreeSwaptionEngineG2SwaptionEngine(per Allow G2SwaptionEngine to price cash-settled (CollateralizedCashPrice) swaptions #2996)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
testFdSwaptionEnginesCashSettlementintest-suite/bermudanswaption.cpp: for bothFdHullWhiteSwaptionEngineandFdG2SwaptionEngine, prices the same European swaption underSettlement::PhysicalandSettlement::Cash+CollateralizedCashPriceand checks the NPVs match, and confirmsCash+ParYieldCurveis now correctly rejected."exception Error expected but not raised"on theParYieldCurvecase, for both engines) and passes with the fix.BermudanSwaptionTestssuite — all pass.🤖 Co-worked with Claude Code