Skip to content

Stabilize Barone-Adesi-Whaley pricing at zero rates - #261

Open
kadyrbekovhamit-cyber wants to merge 1 commit into
domokane:masterfrom
kadyrbekovhamit-cyber:codex/audit-financepy-next
Open

kadyrbekovhamit-cyber wants to merge 1 commit into
domokane:masterfrom
kadyrbekovhamit-cyber:codex/audit-financepy-next

Conversation

@kadyrbekovhamit-cyber

@kadyrbekovhamit-cyber kadyrbekovhamit-cyber commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

baw_value() evaluates the Barone–Adesi–Whaley coefficient through

mm = 2.0 * r / v2
kk = 1.0 - np.exp(-r * t)
mm / kk

At an exactly zero risk-free rate this is 0/0, so a zero-rate American call with a positive dividend yield and a zero-rate American put both raise ZeroDivisionError. For very small positive rates, direct subtraction also loses relative accuracy and the put boundary can need more than the current 50 secant iterations.

This change evaluates the ratio with expm1 and its analytic limit

(2r/v²) / (1-exp(-rt)) → 2/(v²t)  as r → 0.

For a zero-rate put with a non-negative dividend yield, the European put already dominates immediate exercise, so that exact case returns the European value. The put boundary iteration cap is raised from 50 to 100 for the nearby positive-rate cases; the solver and tolerance are otherwise unchanged.

Released-wheel reproduction

Using the published financepy-1.0.1-py3-none-any.whl with synthetic inputs S=K=100, T=1, sigma=20%:

American call, r=0, q=2%      -> ZeroDivisionError
American put,  r=0, q=0       -> ZeroDivisionError
American put,  r=1e-12, q=0   -> FinError: Failed to converge

After this change:

American call, r=0, q=2%      -> 7.0982577675
American call, r=1e-12, q=2%  -> 7.0982578704
American put,  r=0, q=0       -> 7.9655792417
American put,  r=1e-12, q=0   -> 7.9655792416

An independent 2,000-step CRR tree gives 7.1102511896 for the zero-rate call; the BAW difference is about 0.012, consistent with an approximation rather than a singular result. At r=q=0, the American put equals FinancePy's European value 7.9655792417. A separate standard-erf Black–Scholes control gives 7.9655674554; that roughly 1.18e-5 normal-CDF approximation difference predates this change and is outside its scope.

Validation

  • python -m pytest unit_tests/test_FinModelBlackScholes.py -q: 4 passed
  • python -m pytest unit_tests -q: 959 passed, with four pre-existing LSMC RankWarning messages
  • Running the new cases against the unmodified published wheel reproduces the two division errors and the near-zero convergence failure.
  • Four bounded GitHub searches found no matching open or closed report.

All examples are synthetic. This PR makes no claim about a downstream system, position, deployment, or financial loss.

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.

1 participant