Add retry with exponential backoff for yfinance rate limits

When yfinance hits Yahoo Finance rate limits (429 Too Many Requests),
the data fetch fails immediately and the Market Analyst agent loops
endlessly. This adds a simple retry wrapper with exponential backoff
(2s, 4s, 8s) that catches rate limit errors and waits before retrying.

Addresses #415
This commit is contained in:
Charlie Tonneslan 2026-03-22 12:43:44 -04:00
parent f362a160c3
commit 781dd971ba
1 changed files with 29 additions and 2 deletions

View File

@ -1,10 +1,37 @@
from typing import Annotated
from datetime import datetime
from dateutil.relativedelta import relativedelta
import time
import logging
import yfinance as yf
import os
from .stockstats_utils import StockstatsUtils, _clean_dataframe
logger = logging.getLogger(__name__)
def _yf_retry(func, max_retries=3, initial_delay=2.0):
"""Retry a yfinance call with exponential backoff on rate limit errors."""
delay = initial_delay
last_error = None
for attempt in range(max_retries + 1):
try:
return func()
except Exception as e:
last_error = e
error_str = str(e).lower()
if "rate" in error_str or "too many" in error_str or "429" in error_str:
if attempt < max_retries:
logger.warning(
f"yfinance rate limited, retrying in {delay:.0f}s "
f"(attempt {attempt + 1}/{max_retries})"
)
time.sleep(delay)
delay *= 2
continue
raise
raise last_error
def get_YFin_data_online(
symbol: Annotated[str, "ticker symbol of the company"],
start_date: Annotated[str, "Start date in yyyy-mm-dd format"],
@ -17,8 +44,8 @@ def get_YFin_data_online(
# Create ticker object
ticker = yf.Ticker(symbol.upper())
# Fetch historical data for the specified date range
data = ticker.history(start=start_date, end=end_date)
# Fetch historical data with retry on rate limiting
data = _yf_retry(lambda: ticker.history(start=start_date, end=end_date))
# Check if data is empty
if data.empty: