robinhood_get_position
Retrieve a single current stock position from your Robinhood portfolio by providing the ticker symbol. Useful for quick portfolio checks and analysis.
Instructions
Get one current stock position with a faster single-symbol lookup.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes | Stock ticker symbol (e.g., "HIMS", "AAPL") |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/robinhood_mcp/tools.py:163-227 (handler)The core handler function `get_position()` that executes the single-symbol position lookup logic. It normalizes the symbol, checks cache first, validates the instrument via Robinhood API, fetches open stock positions, matches by instrument URL, computes P&L from the quote, and returns a payload with held/price/quantity/equity/change fields.
def get_position(symbol: str) -> dict[str, Any]: """Get a single position by symbol without rebuilding all holdings. Returns: Dict with held=False if absent, otherwise position details including price, quantity, average_buy_price, equity, percent_change, and equity_change. """ symbol = _normalize_symbol(symbol) cached_positions = _get_positions_cached(time.monotonic()) if cached_positions is not None: cached_position = cached_positions.get(symbol) if isinstance(cached_position, dict): return _position_payload(symbol, cached_position) _validate_symbol_instrument(symbol) return {"symbol": symbol, "held": False} instrument_url = _validate_symbol_instrument(symbol) positions = _safe_call(rh.account.get_open_stock_positions) if not isinstance(positions, list): raise RobinhoodError("Unexpected positions response type") match = next( ( item for item in positions if isinstance(item, dict) and item.get("instrument") == instrument_url ), None, ) if not match: return {"symbol": symbol, "held": False} quote = get_quote(symbol) price = quote.get("last_trade_price") or quote.get("mark_price") quantity = match.get("quantity") average_buy_price = match.get("average_buy_price") if price in (None, "") or quantity in (None, "") or average_buy_price in (None, ""): raise RobinhoodError(f"Incomplete position data for symbol: {symbol}") try: quantity_f = float(quantity) price_f = float(price) average_buy_price_f = float(average_buy_price) except (TypeError, ValueError) as e: raise RobinhoodError(f"Invalid numeric position data for symbol: {symbol}") from e equity = quantity_f * price_f equity_change = equity - (quantity_f * average_buy_price_f) percent_change = ( 0.0 if average_buy_price_f == 0.0 else ((price_f - average_buy_price_f) * 100 / average_buy_price_f) ) return _position_payload( symbol, { "price": f"{price_f:.2f}", "quantity": quantity, "average_buy_price": average_buy_price, "equity": f"{equity:.2f}", "percent_change": f"{percent_change:.2f}", "equity_change": f"{equity_change:.2f}", }, ) - src/robinhood_mcp/server.py:121-132 (handler)The MCP-tool-annotated function `robinhood_get_position` registered as an MCP tool. It calls `_ensure_logged_in()` then delegates to the `get_position(symbol)` from tools.py.
@mcp.tool() def robinhood_get_position(symbol: str) -> dict: """Get one current stock position with a faster single-symbol lookup. Args: symbol: Stock ticker symbol (e.g., "HIMS", "AAPL") Returns a dict with held=False if absent, otherwise the position details for that symbol including quantity, price, average buy price, and P&L. """ _ensure_logged_in() return get_position(symbol) - src/robinhood_mcp/server.py:121-132 (registration)The @mcp.tool() decorator on line 99 registers `robinhood_get_position` as a tool named 'robinhood_get_position' in the FastMCP server.
@mcp.tool() def robinhood_get_position(symbol: str) -> dict: """Get one current stock position with a faster single-symbol lookup. Args: symbol: Stock ticker symbol (e.g., "HIMS", "AAPL") Returns a dict with held=False if absent, otherwise the position details for that symbol including quantity, price, average buy price, and P&L. """ _ensure_logged_in() return get_position(symbol) - src/robinhood_mcp/server.py:121-132 (schema)The tool schema includes a single parameter `symbol: str` (line 122) as defined in the function signature. The docstring describes input (stock ticker) and output (dict with held flag and position details).
@mcp.tool() def robinhood_get_position(symbol: str) -> dict: """Get one current stock position with a faster single-symbol lookup. Args: symbol: Stock ticker symbol (e.g., "HIMS", "AAPL") Returns a dict with held=False if absent, otherwise the position details for that symbol including quantity, price, average buy price, and P&L. """ _ensure_logged_in() return get_position(symbol) - src/robinhood_mcp/tools.py:49-57 (helper)Helper function `_normalize_symbol` used by `get_position` to validate and normalize the ticker symbol (uppercase, strip whitespace, non-empty check).
def _normalize_symbol(symbol: str) -> str: """Normalize and validate ticker symbols.""" if not symbol or not isinstance(symbol, str): raise RobinhoodError("Symbol must be a non-empty string") symbol = symbol.upper().strip() if not symbol: raise RobinhoodError("Symbol must be a non-empty string") return symbol - src/robinhood_mcp/tools.py:150-161 (helper)Helper function `_validate_symbol_instrument` used by `get_position` to resolve a symbol to its instrument URL via Robinhood API. Used to match positions and detect unknown tickers.
def _validate_symbol_instrument(symbol: str) -> str: """Resolve a symbol to its instrument URL, raising on unknown tickers.""" instruments = _safe_call(rh.stocks.get_instruments_by_symbols, symbol) if ( not isinstance(instruments, list) or not instruments or not isinstance(instruments[0], dict) or not instruments[0].get("url") ): raise RobinhoodError(f"No instrument found for symbol: {symbol}") return instruments[0]["url"] - src/robinhood_mcp/tools.py:143-147 (helper)Helper function `_position_payload` used by `get_position` to build a stable position response dict with a fixed set of fields (price, quantity, average_buy_price, equity, percent_change, equity_change).
def _position_payload(symbol: str, data: dict[str, Any]) -> dict[str, Any]: """Build a stable position response with a fixed set of fields.""" fields = {k: data.get(k) for k in _POSITION_FIELDS} held = all(v not in (None, "") for v in fields.values()) return {"symbol": symbol, "held": held, **fields} - src/robinhood_mcp/tools.py:133-140 (helper)Helper constant `_POSITION_FIELDS` tuple defining the set of fields extracted for a position response.
_POSITION_FIELDS = ( "price", "quantity", "average_buy_price", "equity", "percent_change", "equity_change", ) - src/robinhood_mcp/tools.py:68-89 (helper)Helper function `_get_positions_cached` used by `get_position` to retrieve cached holdings snapshot when fresh (within 30s TTL).
def _get_positions_cached(now: float) -> dict[str, dict[str, Any]] | None: """Return cached holdings when still fresh.""" global _positions_cache, _positions_cache_ts with _positions_cache_lock: if _positions_cache is None: return None if (now - _positions_cache_ts) >= _POSITIONS_CACHE_TTL_SECONDS: _positions_cache = None _positions_cache_ts = 0.0 return None return deepcopy(_positions_cache) def _set_positions_cache(positions: dict[str, dict[str, Any]], now: float) -> None: """Store a fresh holdings snapshot for subsequent reads.""" global _positions_cache, _positions_cache_ts with _positions_cache_lock: _positions_cache = deepcopy(positions) _positions_cache_ts = now