|
import requests |
|
import os |
|
from dotenv import load_dotenv |
|
from typing import List, Dict, Any |
|
import logging |
|
|
|
|
|
load_dotenv() |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
FMP_API_KEY = os.getenv("FMP_API_KEY") |
|
if not FMP_API_KEY: |
|
logger.warning("FMP_API_KEY not found. FMP calls will fail.") |
|
|
|
|
|
FMP_BASE_URL = "https://financialmodelingprep.com/api/v3" |
|
|
|
|
|
class FMPError(Exception): |
|
"""Custom exception for FMP API errors.""" |
|
|
|
pass |
|
|
|
|
|
def get_earnings_surprises(ticker: str) -> List[Dict[str, Any]]: |
|
""" |
|
Fetches earnings surprise data for a single ticker from Financial Modeling Prep. |
|
Returns a list of earnings surprise records. |
|
Raises FMPError on API-specific issues. |
|
Raises requests.RequestException on network issues. |
|
""" |
|
if not FMP_API_KEY: |
|
raise FMPError("FMP API Key not configured.") |
|
|
|
endpoint = f"{FMP_BASE_URL}/earning_surprise/{ticker}" |
|
params = {"apikey": FMP_API_KEY} |
|
|
|
logger.info(f"Fetching earnings surprise data for {ticker} from FMP.") |
|
response = requests.get(endpoint, params=params, timeout=30) |
|
response.raise_for_status() |
|
data = response.json() |
|
|
|
if isinstance(data, list): |
|
return data |
|
else: |
|
|
|
logger.error(f"Unexpected FMP response structure for {ticker}: {data}") |
|
|
|
if isinstance(data, dict) and data.get("error"): |
|
raise FMPError(f"FMP API returned error for {ticker}: {data['error']}") |
|
|
|
if isinstance(data, dict) and not data: |
|
logger.warning( |
|
f"FMP API returned empty response for {ticker}, potentially no data." |
|
) |
|
return [] |
|
raise FMPError(f"Unexpected API response structure for {ticker}.") |
|
|