diff --git a/app/services/screener_service.py b/app/services/screener_service.py index 3b1d07b..6b0c2d1 100644 --- a/app/services/screener_service.py +++ b/app/services/screener_service.py @@ -153,7 +153,34 @@ class ScreenerService: def _screen_sync(self, query, offset: int, size: int, sort_field: str, sort_asc: bool) -> dict: """Synchronous yfinance screen() call — must run in executor.""" import yfinance as yf - return yf.screen(query, offset=offset, size=size, sortField=sort_field, sortAsc=sort_asc) + + max_retries = 3 + for attempt in range(max_retries): + try: + return yf.screen(query, offset=offset, size=size, sortField=sort_field, sortAsc=sort_asc) + except Exception as e: + err = str(e).lower() + is_retriable = ( + "too many requests" in err + or "rate limit" in err + or "429" in err + or "401" in err + or "unauthorized" in err + ) + if is_retriable and attempt < max_retries - 1: + delay = (attempt + 1) * 3 # 3s, 6s + logger.warning( + "yfinance screen() error '%s' (attempt %d/%d), retrying in %.1fs", + str(e)[:80], attempt + 1, max_retries, delay, + ) + time.sleep(delay) + continue + if is_retriable: + raise RuntimeError( + "Yahoo Finance is rate limiting this server. " + "Please try again in 30–60 seconds." + ) + raise async def screen_stocks( self,