Source code for py_alpaca_api.stock.metadata

import json

from py_alpaca_api.exceptions import APIRequestError, ValidationError
from py_alpaca_api.http.requests import Requests


[docs] class Metadata: """Market metadata API for condition codes and exchange codes.""" def __init__(self, headers: dict[str, str]) -> None: """Initialize the Metadata class. Args: headers: Dictionary containing authentication headers. """ self.headers = headers self.base_url = "https://data.alpaca.markets/v2/stocks/meta" # Cache for metadata that rarely changes self._exchange_cache: dict[str, str] | None = None self._condition_cache: dict[str, dict[str, str]] = {}
[docs] def get_exchange_codes(self, use_cache: bool = True) -> dict[str, str]: """Get the mapping between exchange codes and exchange names. Args: use_cache: Whether to use cached data if available. Defaults to True. Returns: Dictionary mapping exchange codes to exchange names. Raises: APIRequestError: If the API request fails. """ if use_cache and self._exchange_cache is not None: return self._exchange_cache url = f"{self.base_url}/exchanges" try: response = json.loads( Requests().request(method="GET", url=url, headers=self.headers).text ) except Exception as e: raise APIRequestError(message=f"Failed to get exchange codes: {e!s}") from e if not response: raise APIRequestError(message="No exchange data returned") # Cache the result self._exchange_cache = response return response
[docs] def get_condition_codes( self, ticktype: str = "trade", tape: str = "A", use_cache: bool = True, ) -> dict[str, str]: """Get the mapping between condition codes and condition names. Args: ticktype: Type of conditions to retrieve ("trade" or "quote"). Defaults to "trade". tape: Market tape ("A" for NYSE, "B" for NASDAQ, "C" for other). Defaults to "A". use_cache: Whether to use cached data if available. Defaults to True. Returns: Dictionary mapping condition codes to condition descriptions. Raises: ValidationError: If invalid parameters are provided. APIRequestError: If the API request fails. """ # Validate parameters valid_ticktypes = ["trade", "quote"] if ticktype not in valid_ticktypes: raise ValidationError( f"Invalid ticktype. Must be one of: {', '.join(valid_ticktypes)}" ) valid_tapes = ["A", "B", "C"] if tape not in valid_tapes: raise ValidationError( f"Invalid tape. Must be one of: {', '.join(valid_tapes)}" ) # Check cache cache_key = f"{ticktype}_{tape}" if use_cache and cache_key in self._condition_cache: return self._condition_cache[cache_key] url = f"{self.base_url}/conditions/{ticktype}" params: dict[str, str | bool | float | int] = {"tape": tape} try: response = json.loads( Requests() .request(method="GET", url=url, headers=self.headers, params=params) .text ) except Exception as e: raise APIRequestError( message=f"Failed to get condition codes: {e!s}" ) from e if response is None: raise APIRequestError(message="No condition data returned") # Cache the result self._condition_cache[cache_key] = response return response
[docs] def get_all_condition_codes( self, use_cache: bool = True ) -> dict[str, dict[str, dict[str, str]]]: """Get all condition codes for all tick types and tapes. Args: use_cache: Whether to use cached data if available. Defaults to True. Returns: Nested dictionary with structure: { "trade": { "A": {condition_code: description, ...}, "B": {condition_code: description, ...}, "C": {condition_code: description, ...} }, "quote": { "A": {condition_code: description, ...}, "B": {condition_code: description, ...}, "C": {condition_code: description, ...} } } Raises: APIRequestError: If any API request fails. """ result: dict[str, dict[str, dict[str, str]]] = {} for ticktype in ["trade", "quote"]: result[ticktype] = {} for tape in ["A", "B", "C"]: try: result[ticktype][tape] = self.get_condition_codes( ticktype=ticktype, tape=tape, use_cache=use_cache ) except APIRequestError: # Some tape/ticktype combinations might not be available result[ticktype][tape] = {} return result
[docs] def clear_cache(self) -> None: """Clear all cached metadata. This forces the next request to fetch fresh data from the API. """ self._exchange_cache = None self._condition_cache = {}
[docs] def lookup_exchange(self, code: str) -> str | None: """Look up an exchange name by its code. Args: code: The exchange code to look up. Returns: The exchange name if found, None otherwise. """ exchanges = self.get_exchange_codes() return exchanges.get(code)
[docs] def lookup_condition( self, code: str, ticktype: str = "trade", tape: str = "A" ) -> str | None: """Look up a condition description by its code. Args: code: The condition code to look up. ticktype: Type of condition ("trade" or "quote"). Defaults to "trade". tape: Market tape ("A", "B", or "C"). Defaults to "A". Returns: The condition description if found, None otherwise. """ conditions = self.get_condition_codes(ticktype=ticktype, tape=tape) return conditions.get(code)