140 lines
4.2 KiB
Python
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
|