Keep the RS implied-standard-deviation approximation finite - #2835
Merged
Merged
Conversation
Abhayindia
force-pushed
the
fix/rs-implied-stddev-nan
branch
from
September 29, 2026 17:24
9312ed0 to
3c7b1a3
Compare
lballabio
reviewed
Sep 30, 2026
| const auto sqrtDiff = [](Real g, Real yy) { | ||
| const Real hi = std::sqrt(g + yy), lo = std::sqrt(g - yy); | ||
| return (std::isfinite(hi) && std::isfinite(lo)) ? Real(hi - lo) | ||
| : Real(2.0 * yy / (hi + lo)); |
Owner
There was a problem hiding this comment.
Either hi or lo are not finite here. Shouldn't we replace this with the limit?
Contributor
Author
There was a problem hiding this comment.
Replaced it with the limit - gamma is only non-finite when beta underflows, and sqrt(g+y)-sqrt(g-y) tends to 0 there, so the difference branch now returns 0 directly (the sum branch already yields +inf on its own). Over 1920 gridded cases gamma goes infinite 1182 times, and in every one the price is either exactly the intrinsic value or denormal, so 0 is the limit rather than a fallback.
The finiteness test still reports 27 failures without the guard and none with it.
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.
blackFormulaImpliedStdDevApproximationRSreturns NaN over a sizeable part of ordinary parameter space, andblackFormulaImpliedStdDevLiRSinherits it because it seeds its iteration from this function.Found by round-tripping: price an option with
blackFormula, then feed the price back to each implied-standard-deviation inverse and check the library accepts its own output. Over 1488 priced cases the exact solver, the plain approximation and Chambers are all clean; RS returned a non-finite value 304 times.On a plain grid of forward 1.0, call and put, moneyness 1.01 to 10 and standard deviation 0.01 to 1.5, 11 of the 49 cells were NaN.
Two separate causes
betaunderflows to exactly zero for an in-the-money option at low volatility, sogamma = -pi/2 * log(0)is infinite andsqrt(gamma+y) - sqrt(gamma-y)evaluates asinf - inf. Writing that difference in its conjugate form,2y / (sqrt(g+y) + sqrt(g-y)), is algebraically the same and tends to zero as gamma grows.C = (R2 - (ey-1)^2) * ((ey+1)^2 - R2) / ey2is exactly zero when the price sits on intrinsic, sinceRequalsey - 1there. Rounding can leave it slightly negative instead, for example-8.2e-17at forward 1.0, strike 1/1.1, and thenbetais negative andlog(beta)is NaN. Clamped at zero.Why NaN is the wrong answer rather than an inaccurate one
In the failing cells the price has collapsed onto intrinsic, and
blackFormulaImpliedStdDevreturns exactly0there. After this change RS returns0too:Values that were already finite are unchanged: the conjugate branch is only taken when a square root is not finite, and the clamp only engages where
Cwas negative.Verification
testImpliedStdDevApproximationRSStaysFinitecovers moneyness 1.01 to 10 against standard deviations 0.01 to 1.5, calls and puts. Checked that it fails on master without the library change: 27 of its 28 assertions fail.After the change the 1488-case sweep is clean for RS, and the full test suite passes: 1548 test cases, 614293 assertions.
Related, not addressed here
blackFormulaImpliedStdDevLiRSstill throws on 336 of the 1488 cases. Before this change the failure wasboost::math::quantile ... Probability argument is -nan, propagated from the RS guess. With the guess now finite it fails differently,boost::math::erfc_inv Overflow Error, when the normalized price reaches the 0 or 1 boundary andMaddockInverseCumulativeNormaloverflows. That is a change inside the iteration rather than to a closed-form expression, so I have left it alone; happy to look at it separately if you would like.