{"record":{"id":"cc9a62ab00255674","repo":"OpenBB-finance/OpenBB","slug":"error-underlying-price-must-be-provided-if-underl","errorCode":null,"errorMessage":"Error: underlying_price must be provided if underlying_price is not available","messagePattern":"Error: underlying_price must be provided if underlying_price is not available","errorType":"exception","errorClass":"OpenBBError","httpStatus":null,"severity":"error","filePath":"openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py","lineNumber":552,"sourceCode":"        \"\"\"\n        # pylint: disable=import-outside-toplevel\n        from pandas import Series\n\n        if moneyness is None:\n            moneyness = 0.25\n\n        if 0 < moneyness < 100:\n            moneyness = moneyness / 100\n\n        if moneyness > 100 or moneyness < 0:\n            raise OpenBBError(\n                \"Error: Moneyness must be expressed as a percentage between 0 and 100\"\n            )\n\n        df = self.dataframe\n\n        if underlying_price is None and not hasattr(df, \"underlying_price\"):\n            raise OpenBBError(\n                \"Error: underlying_price must be provided if underlying_price is not available\"\n            )\n\n        if date is not None:\n            date = self._get_nearest_expiration(date)\n            df = df[df.expiration.astype(str) == date]\n            strikes = Series(df.strike.unique().tolist())\n\n        last_price = (\n            underlying_price\n            if underlying_price is not None\n            else df.underlying_price.iloc[0]\n        )\n        strikes = Series(self.strikes)\n\n        upper = last_price * (1 + moneyness)  # type: ignore\n        lower = last_price * (1 - moneyness)  # type: ignore\n        nearest_call = (upper - strikes).abs().idxmin()","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/OpenBB-finance/OpenBB/blob/3e071fcc2cd9f891cac6040ae60296dba76dab46/openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py#L534-L570","documentation":"Raised in OptionsChainsData._get_nearest_otm_strikes (options_chains_properties.py:552). The ITM/OTM boundary calculation needs the underlying spot price; it errors when underlying_price was not passed as an argument AND the DataFrame lacks an 'underlying_price' column (the hasattr check on a DataFrame is effectively a column-membership test). Same family as error 47 but on the moneyness helper path.","triggerScenarios":"Calling filter_data(moneyness=25) (or any consumer of _get_nearest_otm_strikes) on a chains result whose provider omitted underlying_price, without supplying underlying_price explicitly and without setting result.last_price beforehand.","commonSituations":"Greeks/quotes-free providers that return only contract rows; processing saved/cached chains JSON where the spot field was dropped.","solutions":["Set result.last_price = <spot> before calling moneyness-based filters (this injects the underlying_price column into the dataframe)","Or pass underlying_price explicitly to the method that accepts it","Or fetch from a provider that includes underlying_price in the chains payload"],"exampleFix":"# before\ndf = res.filter_data(moneyness=25)  # OpenBBError: underlying_price must be provided...\n\n# after\nres.last_price = 585.20\ndf = res.filter_data(moneyness=25)","handlingStrategy":"validation","validationCode":"if \"underlying_price\" not in res.dataframe.columns and res.last_price is None:\n    if spot is None:\n        raise ValueError(\"need spot price for moneyness filtering\")\n    res.last_price = spot\ndf = res.filter_data(moneyness=25)","typeGuard":"def has_spot_for_moneyness(res) -> bool:\n    return \"underlying_price\" in res.to_df().columns or res.last_price is not None","tryCatchPattern":"from openbb_core.app.model.abstract.error import OpenBBError\n\ntry:\n    df = res.filter_data(moneyness=25)\nexcept OpenBBError as e:\n    if \"underlying_price must be provided\" in str(e):\n        res.last_price = get_spot(symbol)\n        df = res.filter_data(moneyness=25)\n    else:\n        raise","preventionTips":["Set last_price immediately after fetching chains from spot-free providers","Keep a cached quote source next to your chains fetch","Centralize a 'prepare chains result' helper that ensures the spot is present before any analytics"],"tags":["options","moneyness","underlying-price","derivatives"],"backgroundTag":null,"analyzedSha":"3e071fcc2cd9f891cac6040ae60296dba76dab46","analyzedAt":"2026-08-14T23:40:48.960Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}