ZhuLinsen/daily_stock_analysis · error · RateLimitError

efinance 可能被限流: {failure_message}

Error message

efinance 可能被限流: {failure_message}

What it means

Raised as RateLimitError from EfinanceFetcher._fetch_stock_data when _build_history_failure_message classifies the caught exception as 'rate_limit_or_anti_bot' — Eastmoney throttled or bot-challenged the request. Distinct from generic failure (error 151) so callers can back off rather than fail over blindly.

Source

Thrown at data_provider/efinance_fetcher.py:471

                    f"endpoint={EASTMONEY_HISTORY_ENDPOINT}, stock_code={stock_code}, "
                    f"range={beg_date}~{end_date_fmt}, elapsed={api_elapsed:.2f}s"
                )
            
            return df
            
        except Exception as e:
            api_elapsed = time.time() - api_start
            category, failure_message = self._build_history_failure_message(
                stock_code=stock_code,
                beg_date=beg_date,
                end_date=end_date_fmt,
                exc=e,
                elapsed=api_elapsed,
            )

            if category == "rate_limit_or_anti_bot":
                logger.warning(failure_message)
                raise RateLimitError(f"efinance 可能被限流: {failure_message}") from e

            logger.error(failure_message)
            raise DataFetchError(f"efinance 获取数据失败: {failure_message}") from e
    
    def _fetch_etf_data(self, stock_code: str, start_date: str, end_date: str) -> pd.DataFrame:
        """
        获取 ETF 基金历史数据

        Exchange-traded ETFs have OHLCV data just like regular stocks, so we use
        ef.stock.get_quote_history (the stock K-line API) which returns full
        open/high/low/close/volume data.

        Previously this method used ef.fund.get_quote_history which only returns
        NAV data (单位净值/累计净值) without volume or OHLC, causing:
        - Issue #541: 'got an unexpected keyword argument beg'
        - Issue #527: ETF volume/turnover always showing 0

        Args:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Back off and retry after a delay — the fetcher already does random_sleep, so add exponential backoff around the call or reduce batch size.
  2. Let DataFetcherManager fail over to akshare/tushare for the affected codes in this run.
  3. Cache daily bars so repeated analyses within a day don't re-hit Eastmoney.

Example fix

# before
for code in codes:
    df = manager.get_daily_data(code, ...)
# after
import time
for i, code in enumerate(codes):
    try:
        df = manager.get_daily_data(code, ...)
    except RateLimitError:
        time.sleep(30 * (i // 10 + 1))  # exponential-ish backoff
        df = manager.get_daily_data(code, ...)
Defensive patterns

Strategy: retry

Try / catch

from data_provider.base import RateLimitError  # efinance rate-limit signal
for attempt in range(3):
    try:
        df = efinance_fetcher.get_daily_data(code, ...)
        break
    except RateLimitError:
        time.sleep(2 ** attempt * 30)
else:
    df = manager.get_daily_data(code, ...)  # fail over after backoff

Prevention

When it happens

Trigger: ef.stock.get_quote_history raising with HTTP 4xx/5xx patterns, connection resets, or anti-bot payloads during bursty A-share K-line fetching; classification depends on _build_history_failure_message's category rules.

Common situations: Batch jobs fetching hundreds of A-share codes sequentially; running alongside other Eastmoney consumers from the same IP; CI network-smoke runs hitting Eastmoney repeatedly.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/d9772138503615c6. Report an issue: GitHub.