Replace Yahoo Finance with Polygon.io for chart data
- Add Polygon.io API client for reliable historical data access - Update bot to use Polygon instead of Yahoo Finance for charts - Add POLYGON_API_KEY to config and environment example - Polygon free tier: 5 API calls/minute, more reliable than Yahoo - Fallback to FinViz chart URL when Polygon unavailable 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
115
polygon_api.py
Normal file
115
polygon_api.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user