{"record":{"id":"3f8a4359ee1a9a26","repo":"HKUDS/Vibe-Trading","slug":"tenor-years-must-be-strictly-positive-got-tenor","errorCode":null,"errorMessage":"tenor_years must be strictly positive, got {tenor_years}","messagePattern":"tenor_years must be strictly positive, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/credit.py","lineNumber":743,"sourceCode":"def survival_probability_to_hazard_rate(survival_prob: float, tenor_years: float) -> float:\n    \"\"\"Convert a survival probability Q(T) to implied constant hazard rate lambda = -ln(Q(T)) / T.\n\n    Args:\n        survival_prob: Survival probability in (0.0, 1.0].\n        tenor_years: Time horizon in years > 0.\n\n    Returns:\n        Annualised hazard rate lambda.\n\n    Raises:\n        ValueError: If survival_prob is not in (0.0, 1.0] or tenor_years <= 0.\n    \"\"\"\n    survival_prob = _require_finite(survival_prob, \"survival_prob\")\n    tenor_years = _require_finite(tenor_years, \"tenor_years\")\n    if survival_prob <= 0.0 or survival_prob > 1.0:\n        raise ValueError(f\"survival_prob must be in (0.0, 1.0], got {survival_prob}\")\n    if tenor_years <= 0.0:\n        raise ValueError(f\"tenor_years must be strictly positive, got {tenor_years}\")\n    return float(-np.log(survival_prob) / tenor_years)\n\n\ndef cds_price(\n    spread_bps: float,\n    recovery_rate: float = 0.40,\n    tenor_years: float = 5.0,\n    risk_free_rate: float = 0.03,\n    coupon_bps: float = 100.0,\n    notional: float = 10_000_000.0,\n    payment_frequency: int = 4,\n) -> dict:\n    \"\"\"Flat-hazard single-name Credit Default Swap (CDS) valuation engine.\n\n    Computes the implied hazard rate, survival probability curve, Risky Present Value\n    of a Basis Point (RPV01), protection leg PV, premium leg PV, fair par spread,\n    and mark-to-market (MTM) upfront cash payment.\n","sourceCodeStart":725,"sourceCodeEnd":761,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/credit.py#L725-L761","documentation":"survival_probability_to_hazard_rate divides -ln(survival_prob) by tenor_years, so the tenor must be a strictly positive number of years. A zero or negative horizon would cause division by zero or a meaningless negative hazard rate, so it is rejected up front.","triggerScenarios":"Calling survival_probability_to_hazard_rate with tenor_years = 0.0, a negative number, or after _require_finite passes a value that is finite but non-positive (e.g. -1.0).","commonSituations":"Passing tenor in months (e.g. 6) where years are expected is fine, but passing 0 for an at-trade-date calculation, or a negative offset from a date arithmetic bug, triggers it.","solutions":["Ensure tenor_years > 0; convert months/days to years before calling","Fix upstream date math that produces 0 or negative horizons","If a zero horizon is legitimate in your flow, skip the call or handle it as a special case"],"exampleFix":"# before\nh = survival_probability_to_hazard_rate(0.95, 0.0)\n\n# after\nh = survival_probability_to_hazard_rate(0.95, 5.0)","handlingStrategy":"validation","validationCode":"if tenor_years <= 0.0:\n    raise ValueError(f\"tenor_years must be > 0, got {tenor_years}\")\nh = survival_probability_to_hazard_rate(survival_prob, tenor_years)","typeGuard":"def is_valid_tenor(t: float) -> bool:\n    return isinstance(t, (int, float)) and float(t) > 0.0 and math.isfinite(t)","tryCatchPattern":"try:\n    h = survival_probability_to_hazard_rate(sp, t)\nexcept ValueError as e:\n    if 'tenor_years' in str(e):\n        h = 0.0  # degenerate horizon\n    else:\n        raise","preventionTips":["Centralize month->year conversion in one helper","Validate maturity dates before computing horizons","Property-test that horizons are always positive for live trades"],"tags":["credit","hazard-rate","tenor","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"}