{"record":{"id":"0940ee0d891c63f7","repo":"HKUDS/Vibe-Trading","slug":"lgd-must-be-in-0-0-1-0-got-lgd","errorCode":null,"errorMessage":"lgd must be in [0.0, 1.0], got {lgd}","messagePattern":"lgd must be in \\[0\\.0, 1\\.0\\], got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/credit.py","lineNumber":889,"sourceCode":"        ead: Exposure at Default in currency units >= 0.\n        pd: Probability of Default in [0.0, 1.0].\n        lgd: Loss Given Default in [0.0, 1.0].\n\n    Returns:\n        Expected loss amount in currency units.\n\n    Raises:\n        ValueError: If ead < 0, pd not in [0, 1], or lgd not in [0, 1].\n    \"\"\"\n    ead = _require_finite(ead, \"ead\")\n    pd = _require_finite(pd, \"pd\")\n    lgd = _require_finite(lgd, \"lgd\")\n    if ead < 0.0:\n        raise ValueError(f\"ead must be non-negative, got {ead}\")\n    if not (0.0 <= pd <= 1.0):\n        raise ValueError(f\"pd must be in [0.0, 1.0], got {pd}\")\n    if not (0.0 <= lgd <= 1.0):\n        raise ValueError(f\"lgd must be in [0.0, 1.0], got {lgd}\")\n    return float(ead * pd * lgd)\n\n\ndef vasicek_credit_var(\n    ead: float,\n    pd: float,\n    lgd: float,\n    asset_correlation: float,\n    confidence: float = 0.999,\n) -> dict:\n    \"\"\"Vasicek single-factor asymptotic credit risk portfolio model (Basel II/III capital framework).\n\n    Under the Asymptotic Single Risk Factor (ASRF) model, conditional default\n    probability at confidence level alpha is:\n        WCDR(alpha) = Phi( (Phi^{-1}(PD) + sqrt(rho) * Phi^{-1}(alpha)) / sqrt(1 - rho) )\n\n    where rho is the pairwise asset return correlation.\n","sourceCodeStart":871,"sourceCodeEnd":907,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/credit.py#L871-L907","documentation":"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.","triggerScenarios":"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.","commonSituations":"Percent-vs-decimal confusion (60 vs 0.6); mixing up LGD with recovery rate (LGD = 1 - recovery); hardcoded workout assumptions above 100%.","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"],"exampleFix":"# before\nel = expected_loss(1_000_000, pd=0.02, lgd=60)\n\n# after\nel = expected_loss(1_000_000, pd=0.02, lgd=0.60)","handlingStrategy":"validation","validationCode":"lgd = 1.0 - recovery_rate if using_recovery else lgd\nif not 0.0 <= lgd <= 1.0:\n    raise ValueError(f\"lgd out of range: {lgd}\")\nel = expected_loss(ead, pd, lgd)","typeGuard":"def is_valid_lgd(l: float) -> bool:\n    return isinstance(l, (int, float)) and 0.0 <= float(l) <= 1.0","tryCatchPattern":"try:\n    el = expected_loss(ead, pd, lgd)\nexcept ValueError as e:\n    if 'lgd' in str(e):\n        el = expected_loss(ead, pd, 0.45)  # regulatory fallback LGD\n    else:\n        raise","preventionTips":["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"],"tags":["credit","lgd","expected-loss","input-validation","python"],"backgroundTag":"argument-out-of-range","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}