numpy / numpy/numpy-financial

BUG: npf.pmt() algorithm future value sign is flipped.

Open
#130 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
409
Forks
98
Avg merge
10h 43m
Merged PRs (30d)
12

Description

Describe the issue:

When calculating the payment required to end with a remaining open balance, aka with a final_value, the calculated payments are larger than the payments required for a achieving final_value=0.

Workaround is inputting the final_value in a flipped state.

In the current state, if a positive pv means a outstanding loan, a negative fv means a remaining outstanding debt; whilst a positive fv implies having 'overpaid' the loan. This is confusing.

Either the algorithm is wrong, and the sign should be flipped; or the documentation should be updated to clarify that pv and fv signs have opposite meaning.

Reproduce the code example:
import numpy_financial as npf

interest_rate = 0.05 / 12
principal_loan = 200_000
compounding_periods = 360
payment_value_fv0 = npf.pmt(
    interest_rate,
    compounding_periods,
    principal_loan,
    0,
)
payment_value_fv50 = npf.pmt(
    interest_rate,
    compounding_periods,
    principal_loan,
    50_000,
)
payment_value_fvn50 = npf.pmt(
    interest_rate,
    compounding_periods,
    principal_loan,
    -50_000,
)
print("{:.2f}".format(payment_value_fv50))
print("{:.2f}".format(payment_value_fv0))
print("{:.2f}".format(payment_value_fvn50))

assert abs(payment_value_fv50) < abs(payment_value_fv0),\
       "Payment with remaining balance should be lower than fully paying off loan, but found {:.2f} > {:.2f}".format(abs(payment_value_fv50),abs(payment_value_fv0))
Error message:
-1133.72
-1073.64
-1013.57
Traceback (most recent call last):
  File "[...]/debugPmt.py", line 28, in <module>
    assert abs(payment_value_fv50) < abs(payment_value_fv0),\
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: Payment with remaining balance should be lower than fully paying off loan, but found 1133.72 > 1073.64
Runtime information:

2.1.1
3.12.6 (main, Sep 9 2024, 00:00:00) [GCC 14.2.1 20240801 (Red Hat 14.2.1-1)]
[{'numpy_version': '2.1.1',
'python': '3.12.6 (main, Sep 9 2024, 00:00:00) [GCC 14.2.1 20240801 (Red '
'Hat 14.2.1-1)]',
'uname': uname_result(system='Linux', node='[...]', release='6.10.9-200.fc40.x86_64', version='#1 SMP PREEMPT_DYNAMIC Sun Sep 8 17:23:55 UTC 2024', machine='x86_64')},
{'simd_extensions': {'baseline': ['SSE', 'SSE2', 'SSE3'],
'found': ['SSSE3',
'SSE41',
'POPCNT',
'SSE42',
'AVX',
'F16C',
'FMA3',
'AVX2'],
'not_found': ['AVX512F',
'AVX512CD',
'AVX512_KNL',
'AVX512_KNM',
'AVX512_SKX',
'AVX512_CLX',
'AVX512_CNL',
'AVX512_ICL']}},
{'architecture': 'Haswell',
'filepath': '/home/[...]/.local/lib/python3.12/site-packages/numpy.libs/libscipy_openblas64_-ff651d7f.so',
'internal_api': 'openblas',
'num_threads': 8,
'prefix': 'libscipy_openblas',
'threading_layer': 'pthreads',
'user_api': 'blas',
'version': '0.3.27'}]
None

Context for the issue:

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The report names the npf.pmt() entry point but no source file or test. Start by reproducing the provided future-value cases, then inspect the implementation and project tests to determine whether the sign convention is wrong or needs documentation. Done means resolving that choice and validating the reported payment comparisons.

Written by the indexing model from the issue text.

Assessment

Tech stack
numpy, python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.