HKUDS/Vibe-Trading · error · ValueError

pd must be in [0.0, 1.0], got {pd}

Error message

pd must be in [0.0, 1.0], got {pd}

What it means

expected_loss requires pd (probability of default) to be a proper probability in [0.0, 1.0]. Values outside this range are meaningless as probabilities and usually indicate a unit or model bug (e.g. basis points or percentages passed as decimals).

Source

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

    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:
        WCDR(alpha) = Phi( (Phi^{-1}(PD) + sqrt(rho) * Phi^{-1}(alpha)) / sqrt(1 - rho) )

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Convert units: percent/100 or bps/10000 before calling
  2. Clip model outputs to [0,1] with np.clip(pd, 0.0, 1.0) if tiny excursions are expected
  3. Verify rating-to-PD mapping tables produce decimals

Example fix

# before
el = expected_loss(1_000_000, pd=200, lgd=0.6)

# after
el = expected_loss(1_000_000, pd=0.02, lgd=0.6)
Defensive patterns

Strategy: validation

Validate before calling

pd = min(max(pd, 0.0), 1.0)  # clip only tiny numerical excursions
if not 0.0 <= pd <= 1.0:
    raise ValueError(f"pd out of range: {pd}")
el = expected_loss(ead, pd, lgd)

Type guard

def is_valid_probability(p: float) -> bool:
    return isinstance(p, (int, float)) and 0.0 <= float(p) <= 1.0

Try / catch

try:
    el = expected_loss(ead, pd, lgd)
except ValueError as e:
    if 'pd' in str(e):
        raise DataQualityError(f"invalid PD {pd!r} — check units") from e
    raise

Prevention

When it happens

Trigger: Calling expected_loss with pd = 2.0, pd = -0.1, pd = 200 (basis points), or pd = 5 (percent).

Common situations: Passing 2 for 2% instead of 0.02; model outputs that escaped [0,1] due to numerical issues; mixing rating-scale numbers with probabilities.

Related errors


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