HKUDS/Vibe-Trading · error · ValueError
tenor_years must be strictly positive, got {tenor_years}
Error message
tenor_years must be strictly positive, got {tenor_years} What it means
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.
Source
Thrown at agent/src/quantlib/credit.py:743
def survival_probability_to_hazard_rate(survival_prob: float, tenor_years: float) -> float:
"""Convert a survival probability Q(T) to implied constant hazard rate lambda = -ln(Q(T)) / T.
Args:
survival_prob: Survival probability in (0.0, 1.0].
tenor_years: Time horizon in years > 0.
Returns:
Annualised hazard rate lambda.
Raises:
ValueError: If survival_prob is not in (0.0, 1.0] or tenor_years <= 0.
"""
survival_prob = _require_finite(survival_prob, "survival_prob")
tenor_years = _require_finite(tenor_years, "tenor_years")
if survival_prob <= 0.0 or survival_prob > 1.0:
raise ValueError(f"survival_prob must be in (0.0, 1.0], got {survival_prob}")
if tenor_years <= 0.0:
raise ValueError(f"tenor_years must be strictly positive, got {tenor_years}")
return float(-np.log(survival_prob) / tenor_years)
def cds_price(
spread_bps: float,
recovery_rate: float = 0.40,
tenor_years: float = 5.0,
risk_free_rate: float = 0.03,
coupon_bps: float = 100.0,
notional: float = 10_000_000.0,
payment_frequency: int = 4,
) -> dict:
"""Flat-hazard single-name Credit Default Swap (CDS) valuation engine.
Computes the implied hazard rate, survival probability curve, Risky Present Value
of a Basis Point (RPV01), protection leg PV, premium leg PV, fair par spread,
and mark-to-market (MTM) upfront cash payment.
View on GitHub (pinned to 80ffdda44c)
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
Example fix
# before h = survival_probability_to_hazard_rate(0.95, 0.0) # after h = survival_probability_to_hazard_rate(0.95, 5.0)
Defensive patterns
Strategy: validation
Validate before calling
if tenor_years <= 0.0:
raise ValueError(f"tenor_years must be > 0, got {tenor_years}")
h = survival_probability_to_hazard_rate(survival_prob, tenor_years) Type guard
def is_valid_tenor(t: float) -> bool:
return isinstance(t, (int, float)) and float(t) > 0.0 and math.isfinite(t) Try / catch
try:
h = survival_probability_to_hazard_rate(sp, t)
except ValueError as e:
if 'tenor_years' in str(e):
h = 0.0 # degenerate horizon
else:
raise Prevention
- Centralize month->year conversion in one helper
- Validate maturity dates before computing horizons
- Property-test that horizons are always positive for live trades
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- survival_prob must be in (0.0, 1.0], got {survival_prob}
- recovery_rate must be in [0.0, 1.0), got {recovery_rate}
- notional must be strictly positive, got {notional}
- payment_frequency must be positive, got {payment_frequency}
- ead must be non-negative, got {ead}
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/3f8a4359ee1a9a26.
Report an issue: GitHub.