TheAlgorithms/Python · error · ValueError

nominal_annual_percentage_rate must be >= 0

Error message

nominal_annual_percentage_rate must be >= 0

What it means

Raised by apr_interest() when nominal_annual_percentage_rate < 0. A negative APR has no financial meaning for this compounding formula (it would represent a guaranteed-loss product the function does not model), so it is rejected before delegation to compound_interest. The check allows exactly 0 (simple no-growth case).

Source

Thrown at financial/interest.py:108

    >>> apr_interest(0.5, 0.05, 3)
    0.08091115361317736
    >>> apr_interest(10000.0, 0.06, -4)
    Traceback (most recent call last):
        ...
    ValueError: number_of_years must be > 0
    >>> apr_interest(10000.0, -3.5, 3.0)
    Traceback (most recent call last):
        ...
    ValueError: nominal_annual_percentage_rate must be >= 0
    >>> apr_interest(-5500.0, 0.01, 5)
    Traceback (most recent call last):
        ...
    ValueError: principal must be > 0
    """
    if number_of_years <= 0:
        raise ValueError("number_of_years must be > 0")
    if nominal_annual_percentage_rate < 0:
        raise ValueError("nominal_annual_percentage_rate must be >= 0")
    if principal <= 0:
        raise ValueError("principal must be > 0")

    return compound_interest(
        principal, nominal_annual_percentage_rate / 365, number_of_years * 365
    )


if __name__ == "__main__":
    import doctest

    doctest.testmod()

View on GitHub (pinned to f5988cc097)

Solutions

  1. Pass a rate >= 0; use 0 for a zero-interest instrument.
  2. Check the data source for sign conventions (e.g. some APIs report yield changes, not levels, as negative).
  3. If you need negative-growth modelling, apr_interest does not support it — compute it with compound_interest directly after your own validation.

Example fix

# before
apr_interest(10000.0, latest_rate, 3)  # latest_rate == -3.5 from feed

# after
rate = max(0.0, latest_rate) if clamped else latest_rate
if rate < 0:
    raise ValueError(f'unsupported negative APR: {rate}')
apr_interest(10000.0, rate, 3)
Defensive patterns

Strategy: validation

Validate before calling

if apr < 0:
    raise ValueError(f'APR cannot be negative, got {apr}')
apr_interest(principal, apr, years)

Try / catch

try:
    value = apr_interest(p, r, y)
except ValueError as exc:
    if 'nominal_annual_percentage_rate' in str(exc):
        # bad rate data, not a code bug
        log.warning('skipping negative APR %s', r); return None
    raise

Prevention

When it happens

Trigger: Calling apr_interest(10000.0, -3.5, 3.0) or any call where the rate argument is negative. Only reached when number_of_years > 0, since years is validated first.

Common situations: Reading rates from feeds that encode decreases as negatives, mixing up percentage (3.5) and fractional (0.035) conventions with a sign error, or passing a placeholder -1 sentinel meaning 'no rate'.

Related errors


AI-assisted analysis of TheAlgorithms/Python@f5988cc097 (2026-08-14). Data as JSON: /api/errors/31e86375ff7fe76f. Report an issue: GitHub.