yfinance_get_option_chain
Fetch option chain data for a stock including calls and puts with strike prices, expiration dates, bid/ask, volume, open interest, and implied volatility to analyze options pricing and IV surfaces.
Instructions
Fetch option chain data (calls and puts) for a stock with available strike prices.
Returns JSON with calls and/or puts data for each expiration date.
JSON fields include:
- contractSymbol: Option contract identifier
- strike: Strike price
- lastPrice: Last traded price
- bid/ask: Bid and ask prices
- volume: Trading volume
- openInterest: Open interest
- impliedVolatility: Implied volatility (IV)
- inTheMoney: Whether option is ITM
- contractSize: Contract size (REGULAR)
- currency: Currency (USD)
Use this to analyze options pricing, IV surfaces, and strike levels.Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes | Stock ticker symbol (e.g., 'AAPL', 'GOOGL', 'MSFT') | |
| expiration_date | No | Option expiration date in YYYY-MM-DD format. Use the 'yfinance_get_option_dates' tool to find available dates, or omit to fetch all available expiration dates. | |
| option_type | No | Which options to return: 'calls', 'puts', or 'all' (both calls and puts). | all |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/yfmcp/server.py:861-967 (handler)The main tool handler for 'yfinance_get_option_chain'. Function get_option_chain() creates a Ticker, fetches available option dates, and for each requested date calls _fetch_option_chain_for_date() to retrieve calls/puts data.
@mcp.tool( name="yfinance_get_option_chain", annotations=ToolAnnotations( readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True, ), ) async def get_option_chain( symbol: Annotated[str, Field(description="Stock ticker symbol (e.g., 'AAPL', 'GOOGL', 'MSFT')")], expiration_date: Annotated[ str | None, Field( description=( "Option expiration date in YYYY-MM-DD format. " "Use the 'yfinance_get_option_dates' tool to find available dates, " "or omit to fetch all available expiration dates." ) ), ] = None, option_type: Annotated[ OptionChainType, Field(description=("Which options to return: 'calls', 'puts', or 'all' (both calls and puts).")), ] = "all", ) -> str: """Fetch option chain data (calls and puts) for a stock with available strike prices. Returns JSON with calls and/or puts data for each expiration date. JSON fields include: - contractSymbol: Option contract identifier - strike: Strike price - lastPrice: Last traded price - bid/ask: Bid and ask prices - volume: Trading volume - openInterest: Open interest - impliedVolatility: Implied volatility (IV) - inTheMoney: Whether option is ITM - contractSize: Contract size (REGULAR) - currency: Currency (USD) Use this to analyze options pricing, IV surfaces, and strike levels. """ try: ticker = await asyncio.to_thread(yf.Ticker, symbol) except _RETRYABLE_YFINANCE_EXCEPTIONS as exc: return _create_retryable_error_response(f"fetching options for '{symbol}'", exc, {"symbol": symbol}) except Exception as exc: return create_error_response( f"Failed to fetch options for '{symbol}'. Verify the symbol is correct.", error_code="API_ERROR", details={"symbol": symbol, "exception": str(exc)}, ) try: available_dates = await asyncio.to_thread(lambda: ticker.options) except Exception as exc: return _create_option_dates_fetch_error( symbol, exc, f"Failed to fetch option dates for '{symbol}'. The symbol may not have options.", ) if not available_dates: return create_error_response( f"No options available for symbol '{symbol}'. " "This symbol may not have listed options (e.g., ETFs, stocks without options).", error_code="NO_DATA", details={"symbol": symbol}, ) if expiration_date is not None and expiration_date not in available_dates: return create_error_response( f"Invalid expiration date '{expiration_date}' for '{symbol}'. Valid dates: {', '.join(available_dates)}", error_code="INVALID_PARAMS", details={ "symbol": symbol, "expiration_date": expiration_date, "valid_dates": available_dates, }, ) dates_to_fetch = [expiration_date] if expiration_date else list(available_dates) result: dict[str, Any] = {} fetch_errors: list[tuple[str, Exception]] = [] for date in dates_to_fetch: try: date_result = await _fetch_option_chain_for_date(ticker, date, option_type) except Exception as exc: logger.warning("Failed to fetch option chain for {} {}: {}", symbol, date, exc) fetch_errors.append((date, exc)) continue result.update(date_result) if result: return dump_json(result) if fetch_errors: return _create_option_chain_fetch_error(symbol, dates_to_fetch, fetch_errors) return create_error_response( f"No option data retrieved for '{symbol}'.", error_code="NO_DATA", details={"symbol": symbol, "dates_requested": list(dates_to_fetch)}, ) - src/yfmcp/server.py:836-858 (helper)Helper function _fetch_option_chain_for_date() that actually fetches option chain data for a single expiration date from yfinance and formats it as JSON with calls and/or puts.
async def _fetch_option_chain_for_date( ticker: yf.Ticker, date: str, option_type: OptionChainType, ) -> dict[str, Any]: """Fetch option chain for a single expiration date.""" opt = await asyncio.to_thread(lambda d=date: ticker.option_chain(d)) calls_df = opt.calls puts_df = opt.puts date_data: dict[str, Any] = {} if calls_df is not None and not calls_df.empty and option_type in {"all", "calls"}: calls_df = calls_df.copy() calls_df["optionType"] = "CALL" date_data["calls"] = calls_df.to_dict(orient="records") if puts_df is not None and not puts_df.empty and option_type in {"all", "puts"}: puts_df = puts_df.copy() puts_df["optionType"] = "PUT" date_data["puts"] = puts_df.to_dict(orient="records") return {date: date_data} if date_data else {} - src/yfmcp/types.py:82-86 (schema)OptionChainType Literal type defining valid option_type parameter values: 'calls', 'puts', or 'all'.
OptionChainType = Literal[ "calls", "puts", "all", ] - src/yfmcp/server.py:861-868 (registration)The @mcp.tool decorator registers the get_option_chain function as the 'yfinance_get_option_chain' MCP tool with read-only, non-destructive, idempotent, and open-world annotations.
@mcp.tool( name="yfinance_get_option_chain", annotations=ToolAnnotations( readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True, ), - src/yfmcp/server.py:71-116 (helper)Helper function _create_option_chain_fetch_error() for generating structured error responses when option chain fetching fails.
def _create_option_chain_fetch_error( symbol: str, dates_to_fetch: list[str], fetch_errors: list[tuple[str, Exception]], ) -> str: failed_dates = [date for date, _ in fetch_errors] if len(dates_to_fetch) == 1: failed_date, exc = fetch_errors[0] if _is_retryable_yfinance_error(exc): return _create_retryable_error_response( f"fetching option chain for '{symbol}' on '{failed_date}'", exc, {"symbol": symbol, "expiration_date": failed_date}, ) return create_error_response( f"Failed to fetch option chain for '{symbol}' on '{failed_date}'.", error_code="API_ERROR", details={"symbol": symbol, "expiration_date": failed_date, "exception": str(exc)}, ) retryable_exceptions = [exc for _, exc in fetch_errors if _is_retryable_yfinance_error(exc)] if retryable_exceptions: return _create_retryable_error_response( f"fetching option chain for '{symbol}'", _select_retryable_exception(retryable_exceptions), { "symbol": symbol, "dates_requested": dates_to_fetch, "failed_dates": failed_dates, }, ) representative_exception = fetch_errors[0][1] return create_error_response( f"Failed to fetch option chain for '{symbol}' for all requested dates.", error_code="API_ERROR", details={ "symbol": symbol, "dates_requested": dates_to_fetch, "failed_dates": failed_dates, "exception": str(representative_exception), }, )