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.

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.

Appendix: 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)