HKUDS/Vibe-Trading · error · ValueError

ead must be non-negative, got {ead}

Error message

ead must be non-negative, got {ead}

What it means

expected_loss computes EL = EAD x PD x LGD. Exposure at default cannot be negative — a negative exposure is a booking or data error, not an economic quantity in this API — so it is rejected before the multiplication.

Source

Thrown at agent/src/quantlib/credit.py:885

def expected_loss(ead: float, pd: float, lgd: float) -> float:
    """Compute regulatory Expected Loss (EL = EAD * PD * LGD).

    Args:
        ead: Exposure at Default in currency units >= 0.
        pd: Probability of Default in [0.0, 1.0].
        lgd: Loss Given Default in [0.0, 1.0].

    Returns:
        Expected loss amount in currency units.

    Raises:
        ValueError: If ead < 0, pd not in [0, 1], or lgd not in [0, 1].
    """
    ead = _require_finite(ead, "ead")
    pd = _require_finite(pd, "pd")
    lgd = _require_finite(lgd, "lgd")
    if ead < 0.0:
        raise ValueError(f"ead must be non-negative, got {ead}")
    if not (0.0 <= pd <= 1.0):
        raise ValueError(f"pd must be in [0.0, 1.0], got {pd}")
    if not (0.0 <= lgd <= 1.0):
        raise ValueError(f"lgd must be in [0.0, 1.0], got {lgd}")
    return float(ead * pd * lgd)


def vasicek_credit_var(
    ead: float,
    pd: float,
    lgd: float,
    asset_correlation: float,
    confidence: float = 0.999,
) -> dict:
    """Vasicek single-factor asymptotic credit risk portfolio model (Basel II/III capital framework).

    Under the Asymptotic Single Risk Factor (ASRF) model, conditional default
    probability at confidence level alpha is:

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Check your exposure aggregation for sign bugs
  2. If negative values represent short positions/netting, take abs() or handle separately, since this API models gross exposure
  3. Log and quarantine records with ead < 0 in your data pipeline

Example fix

# before
el = expected_loss(ead=-500_000, pd=0.02, lgd=0.6)

# after
el = expected_loss(ead=500_000, pd=0.02, lgd=0.6)
Defensive patterns

Strategy: validation

Validate before calling

if ead < 0.0:
    raise ValueError(f"negative EAD from feed: {ead}")
el = expected_loss(ead, pd, lgd)

Type guard

def is_valid_ead(e: float) -> bool:
    return math.isfinite(e) and e >= 0.0

Try / catch

try:
    el = expected_loss(ead, pd, lgd)
except ValueError as e:
    logger.warning("skipping bad exposure record: %s", e)
    el = 0.0

Prevention

When it happens

Trigger: Calling expected_loss(ead=-1_000_000, ...) or with ead = 0.0-derived negative values from an upstream aggregation.

Common situations: Netting logic that produces negative exposures (which this simple API does not model); sign errors in exposure feeds; passing deltas instead of levels.

Related errors


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