← Research index
INDEPENDENT NUMERICAL AUDIT16 September 2026

FinancePy Vasicek pricing loses the small-mean-reversion limit

At a positive mean-reversion parameter of 1e-8, a finite bond price near 0.6167242 becomes infinity. Across 1,350 positive-parameter scenarios: 527 original discrepancies, zero with a local correction, and the same 527 after restoring the original code.

Xamit Kadirbekov
Xamit KadirbekovIndependent verification · GERO Research
REPRODUCED · LOCAL CORRECTION TESTEDActual Numba CPU execution; pinned source and official FinancePy 1.1.2. Synthetic boundary cases; production impact unmeasured.

Report, archive and English overview

Github · Huggingface · Zenodo · Linkedin · Youtube

DOI: 10.5281/zenodo.22801595. The 34.09-second overview uses synthetic JennyNeural narration and English subtitles timed to the actual speech. Video playback starts only on YouTube.

FinancePy Vasicek: a finite price returned as infinity. Open the English overview on YouTube.

GitHub report and source archive · Maintainer issue #269

Frozen reproduction archive (3.60 MB) · Candidate patch · Source ledger

Xamit Kadirbekov · GERO Research · 16 September 2026

The executed FinancePy models.vasicek_mc.zero_price returns infinity for a finite zero-coupon bond price of approximately 0.6167242197654975. The input mean-reversion parameter is positive: a=1e-8. At a=1e-7, the same component returns 1.3630600530447776 instead of 0.6167242683325149.

The other inputs are r0=0.05, b=0.03, sigma=0.01, and t=10 years. These are synthetic stress conditions, not an observed bank calibration or customer portfolio. Very slow mean reversion is a numerical boundary regime; its frequency in production has not been measured.

Executed implementations

  • Executed official master snapshot: 2b9227fea9d832c4033421d6cd53a54316414fca.
  • Official PyPI 1.1.2 wheel, verified against the SHA256 in current PyPI metadata. The target module is byte-identical to master. The whole source tree and wheel are separately fingerprinted; they are not claimed identical.
  • Actual Numba CPU implementation, preserving the existing fastmath=True decorator. Python 3.12.14, NumPy 2.3.5, Numba 0.62.1, mpmath 1.3.0, macOS 15.5 on arm64; one configured numerical thread.

Exact wrong outputs can depend on the compiler and math library. The reference price and the need for finite output in these stated cases do not depend on the observed direction of floating-point cancellation.

Independent mathematical check

For the rate dynamics documented in the module,

dr = a (b-r) dt + sigma dW,

the integrated rate is Gaussian. Let

B(s) = (1 - exp(-a*s)) / a, continuously extended by B(s)=s at a=0.

Its integrated mean is b*t + (r0-b)*B(t) and integrated variance is sigma^2 * integral_0^t B(s)^2 ds. The bond price is therefore

exp(-integrated_mean + integrated_variance / 2).

The primary oracle evaluates these moments at 80 and 120 decimal digits, using the exact binary values of the input floats. All rounded float64 reference values agree at both precisions. 48 additional numerical quadrature checks evaluate the variance integral independently at both precisions and give the same rounded results. The oracle is in oracle.py; it does not call FinancePy.

At zero mean reversion the limit is exp(-r0*t + sigma^2*t^3/6), independent of b. At zero volatility and r0=b, it is exp(-r0*t). At zero maturity it is 1.

A Vasicek discount factor is not a probability. Gaussian rates can become negative, so a price above 1 is not in itself a defect. The discrepancies here are established against the mathematical price for the same inputs.

Measured results

The primary grid contains 1,350 distinct scenarios with strictly positive mean reversion. An additional 135 zero-mean-reversion limit cases are reported separately. The fixed acceptance threshold was 2e-11 * max(1, abs(reference)), selected before the four-variant matrix.

Implementation Positive-a failures / 1,350 Zero-a failures / 135
Original pinned source 527 135
Local candidate correction 0 0
Restored original source 527 135
Official PyPI 1.1.2 527 135

The 527 positive-a failures include 72 non-finite outputs and 45 zero outputs where the reference price is positive and finite. These are subsets of the 527, not additional independent defects.

All 823 previously passing scenarios remain within the fixed tolerance; 495 retain their exact binary output. The other 328 change by small amounts within tolerance. The corrected series branch intentionally changes the evaluation formula, so unchanged behavior is not claimed bit for bit everywhere.

The original, restored and released variants produce identical raw rows on the tested environment. Original source fingerprints remain unchanged; only the candidate target module differs.

Candidate correction and regression checks

For abs(a*t) < 0.5, the candidate evaluates B using expm1 and the integrated variance with a 20-term polynomial in a*t. The coefficients come from integrating (1-exp(-x))^2; they are not fitted to the test data. The series has a continuous value at a=0. The conventional branch outside this region is retained.

For |a*t| <= 0.5, the exact-arithmetic omitted series tail is less than 2e-22 for the dimensionless variance factor. Floating-point evaluation error is checked by the executed oracle matrix; this tail bound alone is not a floating-point error proof.

One existing Vasicek test plus 18 new regressions: 19 pass on the candidate. The original and restored versions give 11 failures and 8 passes. Tests cover small positive mean reversion, the zero-reversion limit, deterministic constant rates and both sides of the branch boundary. The existing test also contains its original seeded Monte Carlo comparisons; the Monte Carlo routines were not modified or independently audited in this investigation.

git apply to a clean source copy produces the exact candidate bytes. Final pytest entry points explicitly assert the imported source path. Exploratory *-initial-import-run.log files record an earlier test-launcher path mistake; they are retained for provenance and excluded from the final regression result.

Duplicate review and limits

Checked the current target source, official release, 258 upstream issue/PR title-and-body records, three focused GitHub issue searches, 12 target-file history summaries, a focused web search and the current 99-report GERO catalog. No exact report was found in that bounded review. The already reported CIR zero-price case #264 concerns a different model and implementation and is explicitly excluded. Numerical cancellation and stable exponential evaluation are established techniques; this report makes no claim to discover those mathematical methods.

No full FinancePy suite, calibration study, performance benchmark, production bank impact or customer loss was measured. The scope is this analytic pricing function. Other functions in the Vasicek module have not been declared fixed. The candidate is a local proposal, not an upstream-accepted change.

Investigation and preparation were AI-assisted. Upstream FinancePy code retains its original GPLv3 license and attribution. No private records are used.

Reproduction

Use a fresh environment with financepy==1.1.2, mpmath==1.3.0 and compatible Numba/NumPy. Run python minimal_repro.py against the installed package, or use --variant baseline, candidate, mutation, or release with this research directory. The selected environment is in evidence/environment.json.

To replay the complete study, run python reproduce.py --output /path/to/new-directory. The output directory must not exist. The runner makes a separate working copy, regenerates the 80/120-digit oracle cases and 48 quadrature controls, executes four variants, checks archived output hashes, runs the 19 tests and verifies a clean patch application. It never rewrites the frozen evidence. Dependencies are listed in requirements-reproduction.txt; Python 3.12 was tested. The original source, extracted wheel and candidate/restored copies are included. All numerical work runs sequentially with one configured thread.

Source refresh before archival packaging

On 16 September 2026, master advanced to 87779f5e1bd99b1a0d2eae241d2c453488b0c76d. The target financepy/models/vasicek_mc.py remains byte-identical to the executed source. A fresh review of all 258 upstream issue/PR title-and-body records found only the distinct CIR issue #264 among the recorded numerical-stability terms. This is a source and duplicate-status refresh, not a full execution of the new repository revision. See evidence/upstream-review-2026-09-16.json.

A separate portable working copy regenerated all 1,485 oracle rows and 48 quadrature controls, reproduced all four archived raw result files byte for byte, and repeated the 19-test original/candidate/restored sequence. All five minimal variant entry points and clean patch application succeeded. See evidence/portable-full-replay.json. No dependencies were downloaded during this replay.

Publication status update - 16 September 2026

Maintainer report: https://github.com/domokane/FinancePy/issues/269 . The issue is open with no comments at the prepublication check; no acceptance is claimed. Current master remains87779f5 and the official Vasicek target has not changed. The frozen ZIP and its experimental evidence remain unchanged.

Distribution update — 17 September 2026

The report is now public on all six GERO publication channels. The existing maintainer thread includes the public archive links. This distribution update does not add numerical runs or claim upstream acceptance. The frozen evidence ZIP is unchanged.

Archive integrity

SHA-256: 73ae765799146c338fd253ded1c385375d4abf6362188625abda0a2836f101f5

Archive verification · Component licenses