Repository navigation
Speed up the Heston variance mesher for small vol of vol - #2940
Merged
lballabio merged 2 commits intoOct 23, 2026
Merged
Conversation
Once the probability of a point reaches 1-epsilon, the next target probability can only be lower and its quantile is below the current point, so the minimum step is used anyway. The inverse is no longer computed in that case; the resulting grid is the same.
With a small vol of vol, the distribution of the variance can be so narrow that the (size-1) minimum steps of the grid are wider than the range between its quantiles. Most points then end up beyond the 1-epsilon quantile and the grid is degenerate, but finding this out requires the quantiles, which are expensive for large degrees of freedom. A normal approximation of the quantiles now detects these cases before computing them, and the mesher uses the default mesh as it did before the non-central chi-square stopped underflowing.
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.
Follow-up to #2908. After it was merged, HestonModelTests/testAnalyticVsBlack took 21.8 s in the "Check test times" job, over the 20 s limit.
The time goes into FdmHestonVarianceMesher, not into the distribution as such. With a small vol of vol (sigma = 1e-4 in testAnalyticVsBlack, 0.001 or 0.005 in several other Heston tests), the degrees of freedom 4·theta·kappa/sigma² are between about 1e5 and 1e8. Before #2908 the CDF underflowed there, the inverse failed and the mesher went to the default mesh at once. Now the CDF converges, so the mesher computes all the quantiles of the grid: each inverse takes 15 to 46 CDF evaluations, since its bracket starts at df+ncp and doubles, which is very wide compared with the width of the distribution, and each evaluation costs up to about 1 ms at these sizes. In testAnalyticVsBlack the distribution is so narrow that the grid is degenerate in the end, and the mesher uses the default mesh anyway after computing all of it.
Two commits:
I checked both on a sweep of 3024 parameter sets (sigma from 1e-4 to 2, kappa from 0.1 to 5, v0 from 0.01 to 0.2, maturities from 1e-5 to 5 years, size 3, 10 and 100, tAvgSteps 5 and 20):
Test times on my machine (Release, Apple clang, seconds):
The last three are still slower than before #2908 because there the mesher builds proper quantile grids that it couldn't build when the CDF underflowed (one grid of 100 points in testFdAmerican and in testFdVanillaWithDividendsVsCached, 144 grids of 3 and 10 points in testFdmHestonBarrierVsBlackScholes). Getting those back to the old times would need a faster inverse or CDF for large arguments.
I also looked at boost::math::non_central_chi_squared, but it doesn't change the picture: at df = 1e8 its CDF is 1.3 to 1.8 times faster than ours and its quantile about 3.6 times faster, while at small arguments (df = 10, ncp = 50) ours is about 3 times faster; and at df = 1e8 the two CDFs differ by about 2.6e-7. So I left the distribution as it is.
The full test suite passes locally, also with QL_HIGH_RESOLUTION_DATE=ON.