@ -153,7 +153,34 @@ class ScreenerService:
def _screen_sync ( self , query , offset : int , size : int , sort_field : str , sort_asc : bool ) - > dict :
def _screen_sync ( self , query , offset : int , size : int , sort_field : str , sort_asc : bool ) - > dict :
""" Synchronous yfinance screen() call — must run in executor. """
""" Synchronous yfinance screen() call — must run in executor. """
import yfinance as yf
import yfinance as yf
max_retries = 3
for attempt in range ( max_retries ) :
try :
return yf . screen ( query , offset = offset , size = size , sortField = sort_field , sortAsc = sort_asc )
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 %.1f s " ,
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 (
async def screen_stocks (
self ,
self ,