HKUDS/Vibe-Trading · error · ValueError

unrecognised option_type {option_type!r}. Accepted (any case

Error message

unrecognised option_type {option_type!r}. Accepted (any case): {sorted(_CALL_ALIASES)} for a call, {sorted(_PUT_ALIASES)} for a put. An unrecognised type is not defaulted, because defaulting one is how a call leg gets settled as a put.

What it means

normalise_option_type accepts case-insensitive call/put aliases and refuses everything else with an explicit error. The long message is deliberate: defaulting an unrecognised type to 'call' is exactly how a put leg in a structured product gets priced and settled as a call, so the library forces the caller to fix the input.

Source

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

    happened, so an unknown string still raises.

    Args:
        option_type: Caller-supplied option type. Case, surrounding whitespace
            and the aliases above are all accepted.

    Returns:
        Either ``"call"`` or ``"put"``.

    Raises:
        ValueError: If the string matches no known spelling. The message lists
            what is accepted, so the fix does not need a source read.
    """
    folded = str(option_type).strip().lower()
    if folded in _CALL_ALIASES:
        return _CALL
    if folded in _PUT_ALIASES:
        return _PUT
    raise ValueError(
        f"unrecognised option_type {option_type!r}. Accepted (any case): "
        f"{sorted(_CALL_ALIASES)} for a call, {sorted(_PUT_ALIASES)} for a put. "
        "An unrecognised type is not defaulted, because defaulting one is how a "
        "call leg gets settled as a put."
    )


def _intrinsic(S: float, K: float, option_type: str) -> float:
    """Undiscounted exercise value of an option.

    Args:
        S: Underlying spot price.
        K: Strike price.
        option_type: Normalised ``"call"`` or ``"put"``.

    Returns:
        ``max(S - K, 0)`` for a call, ``max(K - S, 0)`` for a put. Always a
        float, even for integer inputs, so the public annotations hold.

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Fix the value to a recognised call/put alias (e.g. 'call' or 'put').
  2. Map upstream codes explicitly before calling: {'C': 'call', 'P': 'put'}.
  3. Add schema validation on incoming trade payloads so a bad type fails at ingestion with full context.

Example fix

# before
price = bs_price(S, K, T, r, sigma, option_type=leg['type'])  # leg['type'] == 'C'

# after
CANON = {'C': 'call', 'P': 'put', 'CALL': 'call', 'PUT': 'put'}
price = bs_price(S, K, T, r, sigma, option_type=CANON[leg['type'].upper()])
Defensive patterns

Strategy: type-guard

Validate before calling

CANON = {'c': 'call', 'call': 'call', 'p': 'put', 'put': 'put'}
opt = CANON.get(str(option_type).strip().lower())
assert opt in ('call', 'put'), f'bad option_type {option_type!r}'

Type guard

def is_valid_option_type(s) -> bool:
    return isinstance(s, str) and str(s).strip().lower() in ('c', 'call', 'p', 'put')

Try / catch

try:
    px = bs_price(S, K, T, r, sigma, option_type)
except ValueError as e:
    if 'unrecognised option_type' in str(e):
        raise TradeValidationError(option_type) from e
    raise

Prevention

When it happens

Trigger: Calling bs_price/implied_volatility/barrier_option_price with option_type='c', 'CALL', or a valid alias works, but values like 'callspread', 'both', '', None stringified as 'None', or non-English terms raise this.

Common situations: Config files with option_type: C/P abbreviations not in the alias table; loops over legs where one leg has a typo or an empty string; deserialised JSON where option_type is null and gets coerced to the string 'None'.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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