HKUDS/Vibe-Trading · error · ValuationError

comps.run_comps: mixed eps_basis across the comp set {sorted

Error message

comps.run_comps: mixed eps_basis across the comp set {sorted(all_bases)} -- every peer and the target must declare the same EPS basis, or the P/E distribution mixes GAAP and adjusted earnings

What it means

Every peer and the target must declare the same eps_basis; mixing basic and diluted EPS across the comp set would make the P/E distribution compare GAAP and adjusted figures inconsistently, so run_comps refuses the mixture.

Source

Thrown at agent/src/quantlib/valuation/comps.py:1182

        MissingInputError: (propagated from `calendarise_metric`) if any
            peer's or the target's fiscal-period data lacks a field
            `calendarisation_policy` needs.
    """
    if calendarisation_policy not in CALENDARISATION_POLICIES:
        raise ValuationError(
            f"comps.run_comps: unknown calendarisation_policy {calendarisation_policy!r}, "
            f"must be one of {CALENDARISATION_POLICIES}"
        )
    if len(peers) == 0:
        raise MissingInputError(("peers",), "comps.run_comps")

    names = [peer.name for peer in peers]
    if len(set(names)) != len(names):
        raise ValuationError(f"comps.run_comps: duplicate peer names in {names}")

    all_bases = {peer.eps_basis for peer in peers} | {target.eps_basis}
    if len(all_bases) > 1:
        raise ValuationError(
            "comps.run_comps: mixed eps_basis across the comp set "
            f"{sorted(all_bases)} -- every peer and the target must declare the "
            "same EPS basis, or the P/E distribution mixes GAAP and adjusted "
            "earnings"
        )

    peer_multiples = tuple(peer_multiple_set(peer, calendarisation_policy) for peer in peers)
    distributions = {
        name: multiple_distribution(name, peer_multiples) for name in MULTIPLE_NAMES
    }

    calendarised_target = {
        "ebitda": calendarise_metric(
            target.ebitda, calendarisation_policy, metric_name="ebitda", company_name=target.name
        ),
        "ebit": calendarise_metric(
            target.ebit, calendarisation_policy, metric_name="ebit", company_name=target.name
        ),

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Standardize: pick one basis (usually diluted) and rebuild all peers/target with it explicitly.
  2. If a vendor only has basic EPS, convert or document the choice rather than mixing.
  3. Assert {p.eps_basis for p in peers} | {target.eps_basis} has exactly one element before run_comps.

Example fix

# before
peers = [CompPeer('A','diluted',...), CompPeer('B','basic',...)]

# after
from dataclasses import replace
peers = [replace(p, eps_basis='diluted') for p in peers]  # after verifying data supports it
Defensive patterns

Strategy: validation

Validate before calling

bases = {p.eps_basis for p in peers} | {target.eps_basis}
if len(bases) > 1:
    raise ValueError(f'mixed eps_basis: {sorted(bases)}')

Type guard

def uniform_eps_basis(target, peers):
    return len({p.eps_basis for p in peers} | {target.eps_basis}) == 1

Try / catch

except ValuationError as e:
    if 'mixed eps_basis' in str(e):
        peers = [replace(p, eps_basis='diluted') for p in peers]

Prevention

When it happens

Trigger: One peer dataclass built with eps_basis='basic' while target/others use 'diluted' — often one row built by different code or a defaulted constructor call.

Common situations: Heterogeneous data ingestion where one vendor reports basic EPS only; a peer added later with a default basis; copy-pasted fixture code diverging from the production path.

Related errors


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