TradingAgents/tradingagents/domains/marketdata/clients/finnhub_client.py

140 lines
4.2 KiB
Python

"""
Finnhub client for financial data access.
"""
import logging
from datetime import date
import finnhub
from ..models import (
CompanyProfile,
InsiderSentimentResponse,
InsiderTransactionsResponse,
ReportedFinancialsResponse,
)
logger = logging.getLogger(__name__)
class FinnhubClient:
"""
Finnhub API client for accessing financial data including fundamental data.
Provides access to:
- Company news
- Insider transactions
- Insider sentiment
- Real-time quotes
- Company profiles
- Financial statements (balance sheet, income statement, cash flow)
"""
def __init__(self, api_key: str):
"""
Initialize Finnhub client with official SDK.
Args:
api_key: Finnhub API key
"""
self.client = finnhub.Client(api_key=api_key)
def get_reported_financials(
self, symbol: str, frequency: str
) -> ReportedFinancialsResponse:
"""
Get reported financials data from Finnhub.
Args:
symbol: Stock symbol (e.g., 'AAPL')
frequency: Reporting frequency ('quarterly' or 'annual')
Returns:
Reported financials data from Finnhub API
"""
try:
# Finnhub SDK expects frequency as 'quarterly' or 'annual'
freq = "quarterly" if frequency.lower() in ["quarterly", "q"] else "annual"
response = self.client.financials_reported(symbol=symbol.upper(), freq=freq)
return ReportedFinancialsResponse.from_dict(response)
except Exception as e:
logger.error(f"Error fetching reported financials for {symbol}: {e}")
raise
def get_insider_transactions(
self, symbol: str, start_date: date, end_date: date
) -> InsiderTransactionsResponse:
"""
Get insider transactions for a company.
Args:
symbol: Stock symbol
start_date: Start date as date object
end_date: End date as date object
Returns:
Insider transaction data
"""
start_str = start_date.isoformat()
end_str = end_date.isoformat()
try:
response = self.client.stock_insider_transactions(
symbol.upper(), _from=start_str, to=end_str
)
if isinstance(response, dict):
return InsiderTransactionsResponse.from_dict(response)
else:
# Return empty response if API returns unexpected format
return InsiderTransactionsResponse(data=[], symbol=symbol.upper())
except Exception as e:
logger.error(f"Error fetching insider transactions for {symbol}: {e}")
raise
def get_insider_sentiment(
self, symbol: str, start_date: date, end_date: date
) -> InsiderSentimentResponse:
"""
Get insider sentiment data for a company.
Args:
symbol: Stock symbol
start_date: Start date as date object
end_date: End date as date object
Returns:
Insider sentiment data
"""
start_str = start_date.isoformat()
end_str = end_date.isoformat()
try:
response = self.client.stock_insider_sentiment(
symbol.upper(), _from=start_str, to=end_str
)
if isinstance(response, dict):
return InsiderSentimentResponse.from_dict(response)
else:
# Return empty response if API returns unexpected format
return InsiderSentimentResponse(data=[], symbol=symbol.upper())
except Exception as e:
logger.error(f"Error fetching insider sentiment for {symbol}: {e}")
raise
def get_company_profile(self, symbol: str) -> CompanyProfile:
"""
Get company profile information.
Args:
symbol: Stock symbol
Returns:
Company profile data
"""
try:
response = self.client.company_profile2(symbol=symbol.upper())
return CompanyProfile.from_dict(response)
except Exception as e:
logger.error(f"Error fetching company profile for {symbol}: {e}")
raise