← Research index · Publication records

INDEPENDENT NUMERICAL AUDIT · 16 SEPTEMBER 2026

FinancePy FXForward: today's foreign-currency NPV uses a future exchange rate

Independent numerical audit by Xamit Kadirbekov / GERO Research. Evidence dated 16 September 2026. Version 1.0.0.

The official FinancePy 1.1.2 wheel and the current fx_forward.py at commit 87779f5e1bd99b1a0d2eae241d2c453488b0c76d contain the same source bytes. FXForward.value() computes a domestic present value, then divides it by the contract's current forward exchange rate to produce npv_for. Today's currency conversion should use the current spot rate. This report uses spot_days=0 throughout to isolate that issue from settlement conventions.

For a contract receiving EUR 100 and paying USD 120 after one year:

Input or result Value
Spot, USD per EUR 1.25
Domestic discount factor 0.95
Foreign discount factor 0.98
Today's USD value 8.50
Today's EUR value from discounted legs 6.80
Released npv_for 6.591836734693896
Corrected npv_for 6.800000000000020

The independent reference discounts each currency leg first:

V_{USD}=100(0.98)(1.25)-120(0.95)=8.50,
\qquad
V_{EUR}=100(0.98)-\frac{120(0.95)}{1.25}=6.80.

Equivalently, today's currency amounts must satisfy npv_dom = spot * npv_for. Dividing a present value by a forward rate mixes the valuation and future dates. The same discounted-leg valuation approach is used in OpenGamma Strata's FX single pricer.

Correction and verification

The correction replaces self.npv_for = v / new_fwd_fx_rate with self.npv_for = v / spot_fx_rate.

The self-contained source and test patch is financepy-fx-forward-with-regression.patch. The code-only patch is financepy-fx-forward-spot-conversion.patch.

Reproduction in a Python 3.12 environment with FinancePy 1.1.2 dependencies:

python -m pip install evidence/financepy-1.1.2-py3-none-any.whl
python prepare_release.py

Then run sequentially:

python -B financepy_fx_forward.py
python -B financepy_integration.py
python -B financepy_regression_check.py

The scripts set numerical thread counts to one and limit CPU time. No production/customer data is involved. The initial attempt to disable Numba globally encountered an import incompatibility with its vectorized functions; the successful native runs use the library's normal Numba support with one worker. That import failure was not counted as a numerical defect.

Duplicate screen and limits

Three GitHub searches returned 2, 3 and 24 records respectively. Titles and available search bodies were inspected; the closest records #244, #241 and #102 were read with their comments. They concern curve calibration or option-model support, rather than converting a present value by a future rate. No direct duplicate was identified in this bounded screen. This is not a worldwide priority claim.

Sources: released package, pinned implementation, pinned existing test. Hashes and exact search queries are in evidence/sources.json and evidence/duplicate-screen.json.

An independent source-level candidate remains: value() discounts to expiry while forward() uses delivery. Nonzero spot lag needs a separate audit and is not corrected here. No bank usage, customer loss, investment advice or performance improvement is asserted.

Reuse and attribution

Report and original audit code: Xamit Kadirbekov / GERO Research, 2026. GPL-3.0-or-later applies to this FinancePy-derived package; upstream notices and the bundled wheel are retained. Public issue-search material is included for attribution and duplicate screening, not as a claim of authorship. No company affiliation or maintainer acceptance is implied.

Archive SHA-256: 3520f92c04a2ebcacc2672521b4bd8f1ecba02bce27b95e14bd681aa559911fe