HKUDS/Vibe-Trading · error · ValueError

FCFFYear(year={self.year}).nopat={self.nopat!r} does not mat

Error message

FCFFYear(year={self.year}).nopat={self.nopat!r} does not match ebit * (1 - tax_rate) = {expected_nopat!r}

What it means

FCFFYear.__post_init__ recomputes nopat = ebit * (1 - tax_rate) and raises ValueError if the stored nopat differs beyond _RECONCILIATION_TOLERANCE. This is an internal reconciliation check preventing inconsistent forecast rows.

Source

Thrown at agent/src/quantlib/valuation/dcf.py:562

    depreciation_amortization: float
    capex: float
    delta_nwc: float
    fcff: float

    def __post_init__(self) -> None:
        """Re-check the NOPAT and FCFF arithmetic.

        Raises:
            ValueError: If ``nopat`` or ``fcff`` do not match their formulas,
                or ``year`` is not a positive integer.
        """
        if self.year < 1:
            raise ValueError(f"FCFFYear.year must be >= 1, got {self.year!r}")
        expected_nopat = self.ebit * (1.0 - self.tax_rate)
        if not math.isclose(
            self.nopat, expected_nopat, rel_tol=_RECONCILIATION_TOLERANCE, abs_tol=1e-9
        ):
            raise ValueError(
                f"FCFFYear(year={self.year}).nopat={self.nopat!r} does not match "
                f"ebit * (1 - tax_rate) = {expected_nopat!r}"
            )
        expected_fcff = (
            self.nopat + self.depreciation_amortization - self.capex - self.delta_nwc
        )
        if not math.isclose(
            self.fcff, expected_fcff, rel_tol=_RECONCILIATION_TOLERANCE, abs_tol=1e-9
        ):
            raise ValueError(
                f"FCFFYear(year={self.year}).fcff={self.fcff!r} does not match the "
                f"bridge total {expected_fcff!r}"
            )


def fcff_bridge(
    *,
    ebit: Sequence[float],

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Compute nopat = ebit * (1 - tax_rate) in the caller and pass the unrounded result
  2. Or build rows via fcff_bridge(), which derives nopat internally
  3. Round inputs (ebit, tax_rate) before the call rather than rounding nopat after

Example fix

# before
FCFFYear(year=1, ebit=100.0, tax_rate=0.25, nopat=75.1, ...)

# after
FCFFYear(year=1, ebit=100.0, tax_rate=0.25, nopat=100.0*(1-0.25), ...)
Defensive patterns

Strategy: validation

Validate before calling

nopat = ebit * (1.0 - tax_rate)
FCFFYear(year=1, ebit=ebit, tax_rate=tax_rate, nopat=nopat, ...)

Try / catch

try:
    FCFFYear(...)
except ValueError as e:
    if 'nopat' in str(e):
        row['nopat'] = row['ebit'] * (1 - row['tax_rate'])
    raise

Prevention

When it happens

Trigger: Constructing FCFFYear with a hand-computed or rounded nopat (e.g. 60 instead of 60.0*0.75=45), or rounding nopat to fewer decimals than the tolerance allows.

Common situations: Importing forecasts from spreadsheets where nopat was independently entered/rounded rather than derived; unit tests building fixtures with approximate numbers.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/e44090c2eee87892. Report an issue: GitHub.