import finnhub import logging from typing import Optional, Dict, Any from .base import StockAPIBase from datetime import datetime import pytz logger = logging.getLogger(__name__) class FinnhubAPI(StockAPIBase): """Finnhub implementation of stock price provider.""" def __init__(self, api_key: str): """ Initialize Finnhub API client. Args: api_key: Finnhub API key """ self.client = finnhub.Client(api_key=api_key) self.api_key = api_key def get_stock_price(self, ticker: str) -> Optional[Dict[str, Any]]: """ Retrieve stock price data from Finnhub. Args: ticker: Stock ticker symbol Returns: Dictionary with stock data or None if unavailable """ try: # Get company name from company profile company_name = None try: profile = self.client.company_profile2(symbol=ticker) company_name = profile.get('name') or ticker.upper() except Exception as e: logger.warning(f"Could not fetch company name for {ticker}: {e}") company_name = ticker.upper() # Get current quote data quote = self.client.quote(ticker) if not quote or quote.get('c') is None or quote.get('c') == 0: logger.warning(f"No data available for ticker: {ticker}") return None current_price = float(quote['c']) # Current price previous_close = float(quote['pc']) # Previous close if current_price == 0 or previous_close == 0: logger.warning(f"Invalid price data for ticker: {ticker}") return None change_dollar = current_price - previous_close change_percent = (change_dollar / previous_close) * 100 # Use MarketHours utility for accurate market status from market_hours import MarketHours market_open = MarketHours.is_market_open() return { 'ticker': ticker.upper(), 'company_name': company_name, 'current_price': round(current_price, 2), 'previous_close': round(previous_close, 2), 'change_dollar': round(change_dollar, 2), 'change_percent': round(change_percent, 2), 'market_open': market_open } except Exception as e: logger.error(f"Error fetching data for {ticker}: {e}") return None def is_available(self) -> bool: """ Check if Finnhub API is accessible. Returns: True if accessible, False otherwise """ try: test_quote = self.client.quote("AAPL") return test_quote is not None and test_quote.get('c') is not None except Exception as e: logger.error(f"Finnhub API unavailable: {e}") return False