HKUDS/Vibe-Trading · error · ValueError
lgd must be in [0.0, 1.0], got {lgd}
Error message
lgd must be in [0.0, 1.0], got {lgd} What it means
expected_loss requires lgd (loss given default) to be in [0.0, 1.0] since it represents the fractional loss on exposure when default occurs. Values outside that range are not valid fractions of a loss.
Source
Thrown at agent/src/quantlib/credit.py:889
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) )
where rho is the pairwise asset return correlation.
View on GitHub (pinned to 80ffdda44c)
Solutions
- Use decimals: lgd = 0.60 not 60
- If you have recovery rate R, pass lgd = 1.0 - R
- Keep LGD and recovery clearly named in your config to avoid swaps
Example fix
# before el = expected_loss(1_000_000, pd=0.02, lgd=60) # after el = expected_loss(1_000_000, pd=0.02, lgd=0.60)
Defensive patterns
Strategy: validation
Validate before calling
lgd = 1.0 - recovery_rate if using_recovery else lgd
if not 0.0 <= lgd <= 1.0:
raise ValueError(f"lgd out of range: {lgd}")
el = expected_loss(ead, pd, lgd) Type guard
def is_valid_lgd(l: float) -> bool:
return isinstance(l, (int, float)) and 0.0 <= float(l) <= 1.0 Try / catch
try:
el = expected_loss(ead, pd, lgd)
except ValueError as e:
if 'lgd' in str(e):
el = expected_loss(ead, pd, 0.45) # regulatory fallback LGD
else:
raise Prevention
- Never store LGD in percent; keep decimals everywhere
- Distinguish recovery_rate and lgd fields explicitly in schemas
- Add schema validation (pydantic Field(ge=0, le=1)) for loss parameters
When it happens
Trigger: Calling expected_loss with lgd = 1.2, lgd = -0.1, lgd = 60 (percent), or accidentally passing recovery rate (0.4) when a 60% loss was intended.
Common situations: Percent-vs-decimal confusion (60 vs 0.6); mixing up LGD with recovery rate (LGD = 1 - recovery); hardcoded workout assumptions above 100%.
Related errors
- ead must be non-negative, got {ead}
- pd must be in [0.0, 1.0], got {pd}
- survival_prob must be in (0.0, 1.0], got {survival_prob}
- tenor_years must be strictly positive, got {tenor_years}
- recovery_rate must be in [0.0, 1.0), got {recovery_rate}
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/0940ee0d891c63f7.
Report an issue: GitHub.