HKUDS/Vibe-Trading · error · ValueError

ead must be strictly positive, got {ead}

Error message

ead must be strictly positive, got {ead}

What it means

vasicek_credit_var computes portfolio credit VaR under the Vasicek single-factor model and requires ead (exposure at default) to be strictly positive, since VaR is scaled directly by exposure and a non-positive exposure makes the quantile meaningless.

Source

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

    Returns:
        dict with keys:
            * ``expected_loss`` (float): Base expected loss (EL).
            * ``wcdr`` (float): Worst-case conditional default rate at confidence.
            * ``worst_case_loss`` (float): Total portfolio loss at confidence (WCL).
            * ``unexpected_loss`` (float): Economic capital / Credit VaR (WCL - EL).
            * ``capital_ratio`` (float): Capital required as decimal fraction of EAD.

    Raises:
        ValueError: If parameters violate domain constraints.
    """
    ead = _require_finite(ead, "ead")
    pd = _require_finite(pd, "pd")
    lgd = _require_finite(lgd, "lgd")
    asset_correlation = _require_finite(asset_correlation, "asset_correlation")
    confidence = _require_finite(confidence, "confidence")
    if ead <= 0.0:
        raise ValueError(f"ead must be strictly positive, 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}")
    if not (0.0 <= asset_correlation < 1.0):
        raise ValueError(f"asset_correlation must be in [0.0, 1.0), got {asset_correlation}")
    if not (0.0 < confidence < 1.0):
        raise ValueError(f"confidence must be in (0.0, 1.0), got {confidence}")

    rho = asset_correlation
    inv_pd = float(norm.ppf(pd))
    inv_conf = float(norm.ppf(confidence))

    numerator = inv_pd + np.sqrt(rho) * inv_conf
    denominator = np.sqrt(1.0 - rho)
    wcdr = float(norm.cdf(numerator / denominator))

    el = expected_loss(ead, pd, lgd)

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Filter out zero-exposure entities before calling
  2. Use a positive aggregate exposure for the portfolio
  3. Route negative net exposures through a separate netting-aware model

Example fix

# before
var = vasicek_credit_var(ead=0.0, pd=0.02, lgd=0.6, asset_correlation=0.2, confidence=0.999)

# after
var = vasicek_credit_var(ead=1_000_000.0, pd=0.02, lgd=0.6, asset_correlation=0.2, confidence=0.999)
Defensive patterns

Strategy: validation

Validate before calling

if ead <= 0.0:
    raise ValueError(f"vasicek VaR needs positive exposure, got {ead}")
var = vasicek_credit_var(ead, pd, lgd, asset_correlation, confidence)

Type guard

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

Try / catch

try:
    var = vasicek_credit_var(ead, pd, lgd, rho, conf)
except ValueError as e:
    logger.warning("skipping entity in VaR batch: %s", e)
    var = 0.0

Prevention

When it happens

Trigger: Calling vasicek_credit_var with ead = 0.0 or negative ead; note expected_loss allows ead = 0 but this stricter function does not.

Common situations: Zero exposures from filtered portfolios; reusing validation logic from expected_loss (which permits 0) and assuming the same here; negative net exposures from netting engines.

Related errors


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