HKUDS/Vibe-Trading · error · ValueError

T must be > 0 to imply a volatility, got {T}

Error message

T must be > 0 to imply a volatility, got {T}

What it means

implied_volatility solves for the sigma that reproduces a market price, and with T <= 0 the option has expired or the time argument is invalid — variance scales with T, so no positive volatility can be identified (and the pricer degenerates). The function demands strictly positive time to maturity before attempting any solve.

Source

Thrown at agent/src/quantlib/options.py:443

        ``tol`` and any of them would "converge". A 20-day call struck at half
        the spot prices identically to 16 decimal places for every ``sigma``
        from 0.05 to 0.60, so a solver that answers there is reporting the
        arbitrary endpoint of its own search, not a market volatility. This
        function refuses that: it checks vega at the candidate solution and
        returns ``nan`` when the price carries no volatility information. That
        is a property of the quote rather than a solver failure, and no
        price-tolerance method can do better -- but a confident wrong number is
        worse than an admitted absence.

    Raises:
        ValueError: If ``option_type`` is invalid, if ``T``, ``S`` or ``K`` is
            non-positive, or if ``market_price`` lies outside the no-arbitrage
            interval, which includes the intrinsic-value violation
            ``market_price < discounted intrinsic``.
    """
    option_type = normalise_option_type(option_type)
    if T <= 0:
        raise ValueError(f"T must be > 0 to imply a volatility, got {T}")
    if S <= 0 or K <= 0:
        raise ValueError(f"S and K must be > 0, got S={S}, K={K}")

    lower, upper = _no_arbitrage_bounds(S, K, T, r, option_type, q)
    if market_price < lower - tol:
        raise ValueError(
            f"market price {market_price} is below intrinsic value {lower}"
        )
    if market_price >= upper:
        raise ValueError(
            f"market price {market_price} is at or above the no-arbitrage "
            f"ceiling {upper}; no implied volatility exists"
        )

    def identified(candidate: float) -> float:
        """Return the candidate only if the quote actually pins it down.

        The test is whether one volatility point of movement shifts the price by

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Check the date arithmetic: use precise year fractions (e.g. act/365 with intraday time) and verify expiry > as_of.
  2. Filter out expired contracts before the implied-vol loop.
  3. If T is legitimately tiny, use a minimum floor like T = max(T, 1/365/24) only if your risk convention allows approximation, or price at intrinsic instead.

Example fix

# before
T = (expiry - today).days / 365.0  # 0.0 when expiry == today
iv = implied_volatility(px, S, K, T, r, 'call')

# after
T = (expiry - today).days / 365.0
if T <= 0:
    skip('contract expired')
iv = implied_volatility(px, S, K, T, r, 'call')
Defensive patterns

Strategy: validation

Validate before calling

assert T > 0, f'expired or invalid T={T}'
if T <= 0:
    handle_expired(contract)

Type guard

def has_positive_maturity(T: float) -> bool:
    return isinstance(T, (int, float)) and T > 0

Try / catch

try:
    iv = implied_volatility(px, S, K, T, r, option_type)
except ValueError as e:
    if 'T must be > 0' in str(e):
        mark_expired(contract_id)
    else:
        raise

Prevention

When it happens

Trigger: Calling implied_volatility(..., T=0) or T=-0.1; computing T as (expiry - today).days / 365 when expiry is today or in the past due to a date bug or timezone offset.

Common situations: Day-count bugs where T rounds to 0 for near-dated options; expiry dates parsed with the wrong timezone making today > expiry; stale market data feeds containing already-expired contracts.

Related errors


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