HKUDS/Vibe-Trading · error · ValueError
S and K must be > 0, got S={S}, K={K}
Error message
S and K must be > 0, got S={S}, K={K} What it means
The Black-Scholes formula requires a strictly positive spot S and strike K (log-normal dynamics need log(S/K)). implied_volatility enforces this before building no-arbitrage bounds, because a non-positive S or K makes both the pricing formula and the intrinsic-value interval mathematically meaningless.
Source
Thrown at agent/src/quantlib/options.py:445
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
more than the tolerance the solve was run to. If it does not, then a
whole band of volatilities reprices within ``tol`` and whichever one theView on GitHub (pinned to 80ffdda44c)
Solutions
- Audit the argument order and the source columns feeding S and K.
- Drop or quarantine rows with S <= 0 or K <= 0 before the vol solve.
- Assert positivity at data ingestion with a clear error message including the contract ID.
Example fix
# before iv = implied_volatility(px, row['close'], row['volume'], T, r, 'put') # volume in K! # after assert row['close'] > 0 and row['strike'] > 0 iv = implied_volatility(px, row['close'], row['strike'], T, r, 'put')
Defensive patterns
Strategy: validation
Validate before calling
assert S > 0 and K > 0, f'need positive S={S}, K={K}' Type guard
def are_positive_levels(*vals) -> bool:
return all(isinstance(v, (int, float)) and v > 0 for v in vals) Try / catch
try:
iv = implied_volatility(px, S, K, T, r, option_type)
except ValueError as e:
if 'S and K must be > 0' in str(e):
quarantine(contract_id, reason='bad levels')
else:
raise Prevention
- Name keyword args explicitly: implied_volatility(price=..., S=..., K=...).
- Drop rows with non-positive spot/strike in ingestion.
- Beware DataFrame column order when unpacking.
When it happens
Trigger: Calling implied_volatility with S=0 or K=0 (or negatives); passing a null/NaN spot from a failed market-data lookup that was coerced to 0.0.
Common situations: Missing prices defaulting to 0 in a feed join; strike loaded as 0 for a forward or a contract row that is not a vanilla option; DataFrame columns misordered so a volume or bid column lands in K.
Related errors
- Spot, strike, and barrier must be strictly positive, got S={
- unknown barrier type {barrier_type!r}; valid types: {BARRIER
- unrecognised option_type {option_type!r}. Accepted (any case
- T must be > 0 to imply a volatility, got {T}
- market price {market_price} is below intrinsic value {lower}
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/e7f2e4558a25b1bf.
Report an issue: GitHub.