HKUDS/Vibe-Trading · error · ValueError

a kind cannot be both external and internal; overlapping: {'

Error message

a kind cannot be both external and internal; overlapping: {', '.join(sorted(overlap))}

What it means

external_flows classifies each cash-flow kind as either external (crosses the portfolio boundary, affects return) or internal (rebalancing inside the portfolio, ignored). This error fires when a kind appears in both the external and internal sets (or their defaults overlap with your custom sets), making the classification ambiguous.

Source

Thrown at agent/src/quantlib/performance.py:401

            internal, because assuming would understate the flow adjustment and
            return a plausible wrong number.
    """
    if flows is None:
        return ()

    external = (
        DEFAULT_EXTERNAL_KINDS
        if external_kinds is None
        else frozenset(normalize_kind(kind) for kind in external_kinds)
    )
    internal = (
        DEFAULT_INTERNAL_KINDS
        if internal_kinds is None
        else frozenset(normalize_kind(kind) for kind in internal_kinds)
    )
    overlap = external & internal
    if overlap:
        raise ValueError(
            "a kind cannot be both external and internal; overlapping: "
            f"{', '.join(sorted(overlap))}"
        )

    selected: list[tuple[date, float]] = []
    for flow in flows:
        if flow.is_valuation:
            continue
        if flow.kind in external:
            selected.append((flow.date, -flow.amount))
        elif flow.kind not in internal:
            raise ValueError(
                f"cash-flow kind {flow.kind!r} on {flow.date} is classified "
                "neither external nor internal, so it cannot be treated as "
                "either. Pass external_kinds=[...] or internal_kinds=[...] to "
                "say which side of the portfolio boundary it crosses. Known "
                f"external: {', '.join(sorted(external))}. Known internal: "
                f"{', '.join(sorted(internal))}."

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Remove the overlapping kind(s) listed in the error message from one of the two sets.
  2. Prefer overriding only one of external_kinds/internal_kinds and let the defaults supply the other, after checking the shipped default sets.
  3. Assert disjointness in your config before calling: external & internal == set().

Example fix

# before
external_flows(flows,
    external_kinds=['dividend', 'fee'],
    internal_kinds=['fee', 'rebalance'],  # 'fee' in both -> ValueError
)

# after
external_flows(flows,
    external_kinds=['dividend', 'fee'],
    internal_kinds=['rebalance'],
)
Defensive patterns

Strategy: validation

Validate before calling

ext, intl = set(external_kinds or DEFAULT_EXTERNAL_KINDS), set(internal_kinds or DEFAULT_INTERNAL_KINDS)
assert not (ext & intl), f'overlap: {sorted(ext & intl)}'

Try / catch

try:
    flows_ext = external_flows(flows, external_kinds=ext, internal_kinds=intl)
except ValueError as e:
    if 'both external and internal' in str(e):
        overlap = ext & intl
        intl -= overlap  # resolve: external wins
        flows_ext = external_flows(flows, external_kinds=ext, internal_kinds=intl)
    else:
        raise

Prevention

When it happens

Trigger: Calling external_flows, time_weighted_return, modified_dietz_return, or money_weighted_return and passing external_kinds={'dividend'} while 'dividend' is also in internal_kinds, or passing a custom set that overlaps with DEFAULT_INTERNAL_KINDS/DEFAULT_EXTERNAL_KINDS.

Common situations: Customizing external_kinds to include 'fee' or 'dividend' without removing it from internal_kinds; copying classification lists from another system where the same label means different things; defaults changing across library versions so a previously-fine custom list now overlaps.

Related errors


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