OpenBB-finance/OpenBB · error · OpenBBError

Error: No premium data found for the selected strikes. Call:

Error message

Error: No premium data found for the selected strikes. Call: {bought}, Put: {sold}

What it means

Raised by OptionsChainsData.synthetic_long() when the put premium lookup at the sold put strike (bid column) or the call premium lookup at the bought call strike (ask column) returns an empty series in the expiration slice. Both legs must be quotable to compute position cost and breakeven.

Source

Thrown at openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py:1260

            days = -1

        dte_estimate = self._get_nearest_expiration(days)
        chains = DataFrame(chains[chains["expiration"].astype(str) == dte_estimate])
        last_price = (
            underlying_price
            if underlying_price is not None
            else chains.underlying_price.iloc[0]
        )
        bid = self._identify_price_col(chains, "put", "bid")
        ask = self._identify_price_col(chains, "call", "ask")
        strike_price = last_price if strike == 0 else strike
        sold = self._get_nearest_strike("put", days, strike_price, bid, False)
        bought = self._get_nearest_strike("call", days, strike_price, ask, False)
        put_premium = chains[chains.strike == sold].query("`option_type` == 'put'")[bid]  # type: ignore
        call_premium = chains[chains.strike == bought].query("`option_type` == 'call'")[ask]  # type: ignore

        if call_premium.empty or put_premium.empty:
            raise OpenBBError(
                f"Error: No premium data found for the selected strikes. Call: {bought}, Put: {sold}"
            )

        put_premium = put_premium.values[0] * (-1)
        call_premium = call_premium.values[0]
        dte = chains[chains.expiration.astype(str) == dte_estimate]["dte"].unique()[0]  # type: ignore
        position_cost = call_premium + put_premium
        breakeven = ((sold + bought) / 2) + position_cost  # type: ignore
        synthetic_long_dict: dict = {}
        # Includes the as-of date if it is historical EOD data.
        if hasattr(chains, "eod_date"):
            synthetic_long_dict.update({"Date": chains.eod_date.iloc[0]})

        synthetic_long_dict.update(
            {
                "Symbol": chains.underlying_symbol.unique()[0],
                "Underlying Price": last_price,
                "Expiration": dte_estimate,

View on GitHub (pinned to 3e071fcc2c)

Solutions

  1. Pass an explicit strike that exists in chains.strikes.
  2. Switch to another expiration (days=) with denser quotes.
  3. Use a provider that supplies both bid and ask columns (e.g. cboe/deribit).
  4. Inspect the slice and confirm both legs exist before calling.

Example fix

# before
res = chains.synthetic_long(days=30)  # empty call/put premium at estimated strikes

# after
res = chains.synthetic_long(days=30, strike=152.5, underlying_price=spot)
Defensive patterns

Strategy: validation

Validate before calling

df = chains.dataframe
exp = chains._get_nearest_expiration(days)
s = df[df['expiration'].astype(str) == exp]
assert not s.query("option_type == 'call'")[ask_col].dropna().empty, 'no call ask'
assert not s.query("option_type == 'put'")[bid_col].dropna().empty, 'no put bid'

Type guard

def legs_quotable(df, call_k, put_k) -> bool:
    c = df[(df.strike == call_k) & (df.option_type == 'call')]
    p = df[(df.strike == put_k) & (df.option_type == 'put')]
    return not c.empty and not p.empty

Try / catch

try:
    chains.synthetic_long(days=days)
except OpenBBError as e:
    if 'No premium data found' in str(e):
        chains.synthetic_long(days=days, strike=nearest_listed_strike, underlying_price=spot)

Prevention

When it happens

Trigger: Calling chains.synthetic_long(...) where the expiration chosen by _get_nearest_expiration has no 'put' row at the estimated put strike in the bid column, or no 'call' row at the call strike in the ask column; commonly when bid or ask columns are missing so _identify_price_col falls back to a column that is empty for those rows.

Common situations: Providers that report last_price only (no bid/ask) at ATM strikes; sparse single-sided quotes on illiquid symbols; strike rounding mismatch (e.g. estimated 152.55 vs listed 152.5).

Related errors


AI-assisted analysis of OpenBB-finance/OpenBB@3e071fcc2c (2026-08-14). Data as JSON: /api/errors/af2ee7c4101e2fa8. Report an issue: GitHub.