Skip to main content
Glama

Schwab Model Context Protocol Server

by jkoelker
options.py6.72 kB
# from typing import Annotated import datetime from mcp.server.fastmcp import FastMCP from schwab_mcp.context import SchwabContext, SchwabServerContext from schwab_mcp.tools._registration import register_tool from schwab_mcp.tools.utils import JSONType, call def _parse_date(value: str | datetime.date | None) -> datetime.date | None: if value is None: return None if isinstance(value, datetime.date) and not isinstance(value, datetime.datetime): return value if isinstance(value, datetime.datetime): return value.date() return datetime.datetime.strptime(value, "%Y-%m-%d").date() async def get_option_chain( ctx: SchwabContext, symbol: Annotated[str, "Symbol of the underlying security (e.g., 'AAPL', 'SPY')"], contract_type: Annotated[ str | None, "Type of option contracts: CALL, PUT, or ALL (default)" ] = None, strike_count: Annotated[ int, "Number of strikes above/below the at-the-money price (default: 25)", ] = 25, include_quotes: Annotated[ bool | None, "Include underlying and option market quotes" ] = None, from_date: Annotated[ str | datetime.date | None, "Start date for option expiration ('YYYY-MM-DD' or datetime.date)", ] = None, to_date: Annotated[ str | datetime.date | None, "End date for option expiration ('YYYY-MM-DD' or datetime.date)", ] = None, ) -> JSONType: """ Returns option chain data (strikes, expirations, prices) for a symbol. Use for standard chains. Params: symbol, contract_type (CALL/PUT/ALL), strike_count (default 25), include_quotes (bool), from_date (YYYY-MM-DD), to_date (YYYY-MM-DD). Limit data returned using strike_count and date parameters. """ context: SchwabServerContext = ctx.request_context.lifespan_context client = context.options from_date_obj = _parse_date(from_date) to_date_obj = _parse_date(to_date) return await call( client.get_option_chain, symbol, contract_type=client.Options.ContractType[contract_type.upper()] if contract_type else None, strike_count=strike_count, include_underlying_quote=include_quotes, from_date=from_date_obj, to_date=to_date_obj, ) async def get_advanced_option_chain( ctx: SchwabContext, symbol: Annotated[str, "Symbol of the underlying security"], contract_type: Annotated[ str | None, "Type of contracts: CALL, PUT, or ALL (default)" ] = None, strike_count: Annotated[ int, "Number of strikes above/below the at-the-money price (default: 25)", ] = 25, include_quotes: Annotated[bool | None, "Include quotes for the options"] = None, strategy: Annotated[ str | None, ( "Option strategy: SINGLE (default), ANALYTICAL, COVERED, VERTICAL, CALENDAR, STRANGLE, STRADDLE, " "BUTTERFLY, CONDOR, DIAGONAL, COLLAR, ROLL" ), ] = None, interval: Annotated[ str | None, "Strike interval for spread strategy chains" ] = None, strike: Annotated[float | None, "Only return options with the given strike"] = None, strike_range: Annotated[ str | None, "Filter strikes: IN_THE_MONEY, NEAR_THE_MONEY, OUT_OF_THE_MONEY, STRIKES_ABOVE_MARKET, STRIKES_BELOW_MARKET, STRIKES_NEAR_MARKET, ALL (default)", ] = None, from_date: Annotated[ str | datetime.date | None, "Start date for options ('YYYY-MM-DD' or datetime.date)", ] = None, to_date: Annotated[ str | datetime.date | None, "End date for options ('YYYY-MM-DD' or datetime.date)", ] = None, volatility: Annotated[float | None, "Volatility for ANALYTICAL strategy"] = None, underlying_price: Annotated[ float | None, "Underlying price for ANALYTICAL strategy" ] = None, interest_rate: Annotated[ float | None, "Interest rate for ANALYTICAL strategy" ] = None, days_to_expiration: Annotated[ int | None, "Days to expiration for ANALYTICAL strategy" ] = None, exp_month: Annotated[ str | None, "Expiration month (e.g., JAN) for ANALYTICAL strategy" ] = None, option_type: Annotated[ str | None, "Filter option type: STANDARD, NON_STANDARD, ALL (default)" ] = None, ) -> JSONType: """ Returns advanced option chain data with strategies, filters, and theoretical calculations. Use for complex analysis. Params: symbol, contract_type, strike_count, include_quotes, strategy (SINGLE/ANALYTICAL/etc.), interval, strike, strike_range (ITM/NTM/etc.), from/to_date, volatility/underlying_price/interest_rate/days_to_expiration (for ANALYTICAL), exp_month, option_type (STANDARD/NON_STANDARD/ALL). Limit data returned using strike_count and date parameters. """ context: SchwabServerContext = ctx.request_context.lifespan_context client = context.options from_date_obj = _parse_date(from_date) to_date_obj = _parse_date(to_date) return await call( client.get_option_chain, symbol, contract_type=client.Options.ContractType[contract_type.upper()] if contract_type else None, strike_count=strike_count, include_underlying_quote=include_quotes, strategy=client.Options.Strategy[strategy.upper()] if strategy else None, interval=interval, strike=strike, strike_range=client.Options.StrikeRange[strike_range.upper()] if strike_range else None, from_date=from_date_obj, to_date=to_date_obj, volatility=volatility, underlying_price=underlying_price, interest_rate=interest_rate, days_to_expiration=days_to_expiration, exp_month=client.Options.ExpirationMonth[exp_month.upper()] if exp_month else None, option_type=client.Options.Type[option_type.upper()] if option_type else None, ) async def get_option_expiration_chain( ctx: SchwabContext, symbol: Annotated[str, "Symbol of the underlying security"], ) -> JSONType: """ Returns available option expiration dates for a symbol, without contract details. Lightweight call to find available cycles. Param: symbol. """ context: SchwabServerContext = ctx.request_context.lifespan_context client = context.options return await call(client.get_option_expiration_chain, symbol) _READ_ONLY_TOOLS = ( get_option_chain, get_advanced_option_chain, get_option_expiration_chain, ) def register(server: FastMCP, *, allow_write: bool) -> None: _ = allow_write for func in _READ_ONLY_TOOLS: register_tool(server, func)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jkoelker/schwab-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server