""" Earnings Surprise endpoints — SEC EDGAR XBRL EPS data """ import logging from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import Response from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.schemas.earnings import EarningsSurpriseEntry, EarningsSurpriseResponse from app.services.earnings_service import EarningsService from app.utils.cache import with_cache router = APIRouter() logger = logging.getLogger("app.api.v1.earnings") @router.get( "/surprise/{symbol}", response_model=EarningsSurpriseResponse, summary="Get earnings surprise history", description=( "Quarterly EPS surprise: reported vs analyst consensus estimate.\n\n" "**데이터 소스**: yfinance-plus (`Ticker.earnings_dates`). API 키 불필요.\n" "**커버리지**: ~25분기 (6년+). 첫 조회 시 자동 인덱싱.\n\n" "**surprise** = reported_eps - estimated_eps.\n" "**surprise_percentage** = (surprise / estimated) × 100.\n" "**streak**: 연속 beat (양수) 또는 miss (음수) 횟수." ), ) @with_cache(namespace="earnings:surprise", ttl=None, key_params=["symbol", "quarters"]) async def get_earnings_surprise( symbol: str, response: Response, quarters: int = Query(8, ge=1, le=40, description="Number of recent quarters (max ~25 available)"), force_refresh: bool = Query(False, description="Bypass cache and re-fetch from yfinance"), db: AsyncSession = Depends(get_db), ): svc = EarningsService() if force_refresh: try: await svc.index_earnings(db, symbol, force_refresh=True) except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) try: rows, stats = await svc.get_earnings_surprise(db, ticker=symbol, quarters=quarters) except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Earnings surprise error for {symbol}: {e}") raise HTTPException(status_code=502, detail=f"Failed to fetch earnings data: {e}") entries = [EarningsSurpriseEntry.from_orm_obj(r) for r in rows] return EarningsSurpriseResponse( symbol=symbol.upper(), quarters=entries, streak=stats["streak"], avg_surprise_pct=stats["avg_surprise_pct"], metadata={ "data_source": "SEC_XBRL", "quarters_requested": quarters, "quarters_returned": len(entries), "note": "surprise = current_eps - previous_quarter_eps (QoQ change)", }, )