HKUDS/Vibe-Trading · error · ValueError

FCFFYear.year must be >= 1, got {self.year!r}

Error message

FCFFYear.year must be >= 1, got {self.year!r}

What it means

FCFFYear.__post_init__ validates that year is a positive integer index (>= 1) for projection years. Year 0 or negative indices are rejected because discounting conventions assume years 1..N.

Source

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

    year: int
    ebit: float
    tax_rate: float
    nopat: float
    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}"
            )

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use enumerate(forecasts, start=1) so year starts at 1
  2. Map calendar years to relative offsets (2026 -> year 1) explicitly
  3. Prefer building FCFFYear via fcff_bridge(), which numbers years correctly

Example fix

# before
for i, e in enumerate(ebit):
    FCFFYear(year=i, ...)

# after
for i, e in enumerate(ebit, start=1):
    FCFFYear(year=i, ...)
Defensive patterns

Strategy: validation

Validate before calling

for i, row in enumerate(forecasts, start=1):
    FCFFYear(year=i, ...)

Type guard

def is_projection_year(y) -> TypeGuard[int]:
    return isinstance(y, int) and y >= 1

Try / catch

try:
    FCFFYear(...)
except ValueError as e:
    if 'year' in str(e):
        raise ValueError('projection years must be 1-based') from e
    raise

Prevention

When it happens

Trigger: Constructing FCFFYear(year=0, ...) or FCFFYear(year=-1, ...), typically from a 0-based loop index passed directly as year.

Common situations: enumerate() over forecasts starting at 0 and passing index as year; off-by-one when converting calendar years to relative projection years.

Related errors


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