"""Polygon.io API client for historical stock data.""" import requests import logging from typing import Optional, Dict, List from datetime import datetime, timedelta logger = logging.getLogger(__name__) class PolygonAPI: """Polygon.io implementation for historical stock data.""" def __init__(self, api_key: str): """ Initialize Polygon API client. Args: api_key: Polygon.io API key """ self.api_key = api_key self.base_url = "https://api.polygon.io" def get_candles(self, ticker: str, days: int = 30, resolution: str = 'D') -> Optional[Dict[str, List]]: """ Retrieve historical candlestick data from Polygon.io. Args: ticker: Stock ticker symbol days: Number of days of historical data to fetch resolution: Candle resolution (ignored, always daily) Returns: Dictionary with OHLCV data or None if unavailable """ try: # Calculate date range end_date = datetime.now() start_date = end_date - timedelta(days=days) # Format dates as YYYY-MM-DD from_date = start_date.strftime('%Y-%m-%d') to_date = end_date.strftime('%Y-%m-%d') # Build API URL url = f"{self.base_url}/v2/aggs/ticker/{ticker}/range/1/day/{from_date}/{to_date}" # Make request params = { 'adjusted': 'true', 'sort': 'asc', 'apiKey': self.api_key } logger.info(f"Fetching Polygon data for {ticker} from {from_date} to {to_date}") response = requests.get(url, params=params, timeout=10) response.raise_for_status() data = response.json() # Check if we got results if data.get('status') != 'OK' or not data.get('results'): logger.warning(f"No candle data available for ticker: {ticker}") return None results = data['results'] # Convert to expected format timestamps = [] opens = [] highs = [] lows = [] closes = [] volumes = [] for candle in results: # Polygon returns timestamp in milliseconds timestamps.append(int(candle['t'] / 1000)) opens.append(float(candle['o'])) highs.append(float(candle['h'])) lows.append(float(candle['l'])) closes.append(float(candle['c'])) volumes.append(int(candle['v'])) logger.info(f"Successfully fetched {len(timestamps)} candles for {ticker}") return { 'timestamps': timestamps, 'open': opens, 'high': highs, 'low': lows, 'close': closes, 'volume': volumes } except requests.exceptions.RequestException as e: logger.error(f"HTTP error fetching candle data for {ticker}: {e}") return None except Exception as e: logger.error(f"Error fetching candle data for {ticker}: {e}") return None def is_available(self) -> bool: """ Check if Polygon API is accessible. Returns: True if accessible, False otherwise """ try: # Test with a simple request test_data = self.get_candles("AAPL", days=5) return test_data is not None except Exception as e: logger.error(f"Polygon API unavailable: {e}") return False