Skip to content

Reject payoffAtExpiry exercise in QdPlus/QdFp American engines - #3001

Open
leafyoung wants to merge 1 commit into
lballabio:masterfrom
leafyoung:fix-qdplus-american-engines-payoff-at-expiry
Open

leafyoung wants to merge 1 commit into
lballabio:masterfrom
leafyoung:fix-qdplus-american-engines-payoff-at-expiry

Conversation

@leafyoung

Copy link
Copy Markdown

Summary

detail::QdPutCallParityEngine::calculate() — shared by both QdPlusAmericanEngine and QdFpAmericanEngine — checks only that the exercise type is American:

QL_REQUIRE(arguments_.exercise->type() == Exercise::American,
           "not an American option");

It never inspects AmericanExercise::payoffAtExpiry(). Every other closed-form American engine explicitly rejects that flag, since their analytic formulas assume the standard immediate-early-exercise convention, not the payoff-deferred-to-expiry one:

  • BaroneAdesiWhaleyApproximationEngine
  • BjerksundStenslandApproximationEngine
  • JuQuadraticApproximationEngine
  • McAmericanEngine (via EarlyExercise::payoffAtExpiry())

Without the same guard, QD+/QD-FP silently price a payoffAtExpiry-flagged American option as if it were a standard American option — a confidently wrong result with no indication anything is off, rather than the clean rejection every sibling closed-form engine gives.

Fix

Added the same guard pattern used by the sibling engines (downcast to AmericanExercise, reject if payoffAtExpiry() is set), in the single shared calculate() so both QdPlusAmericanEngine and QdFpAmericanEngine are covered.

Test

Added testQdEnginesRejectPayoffAtExpiry to test-suite/americanoption.cpp, covering both engines: confirms a plain American exercise still prices normally, and a payoffAtExpiry-flagged one throws. Without the fix, both engines compute a result instead of throwing; with the fix, both throw as expected. Full AmericanOptionTests suite (39 cases) passes with no regressions.

🤖 Co-worked with Claude Code

detail::QdPutCallParityEngine::calculate() -- shared by both
QdPlusAmericanEngine and QdFpAmericanEngine -- checked only that the
exercise type is American, never inspecting
AmericanExercise::payoffAtExpiry(). Every other closed-form American
engine (Barone-Adesi-Whaley, Bjerksund-Stensland, Ju quadratic)
explicitly rejects that flag, since their formulas assume the
standard immediate-early-exercise convention, not the
payoff-deferred-to-expiry one. Without the same guard, QD+/QD-FP
silently priced such an option as a standard American option instead
of raising an error.

Added a regression test covering both engines: confirmed it fails
(computes a result instead of throwing) without the fix and passes
with it, while a plain American exercise is unaffected.

Co-worked with Claude Code
Copilot AI balanced review requested due to automatic review settings November 1, 2026 13:53

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.

🟢 Approval recommended

No active review issues remain for the shared QD American exercise-validation change.

No open issues 🎉

What changed in this PR

Adds validation to the shared QD American pricing engine so QD+ and QD-FP reject payoff-at-expiry exercises instead of pricing them under unsupported assumptions.

Changes:

  • Checks the American exercise’s payoffAtExpiry flag in the shared calculation path.
  • Adds regression coverage for both engines, checking that standard American exercises price without throwing and payoff-at-expiry exercises raise an error.
File Description
test-suite/​americanoption.cpp Adds exercise-validation regression coverage for both QD engines.
ql/​pricingengines/​vanilla/​qdplusamericanengine.cpp Rejects unsupported payoff-at-expiry exercises in the shared engine.

Review effort: Balanced

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