ZhuLinsen/daily_stock_analysis · error · DataFetchError

Tushare API 未初始化,请检查 Token 配置

Error message

Tushare API 未初始化,请检查 Token 配置

What it means

DataFetchError raised by TushareFetcher._call_api_with_rate_limit when self._api is None — the Tushare client was never initialized, which happens when the token is missing/empty so the pro API client could not be constructed. It fails before the rate-limit bookkeeping and any remote call.

Source

Thrown at data_provider/tushare_fetcher.py:291

            logger.warning(
                f"Tushare 达到速率限制 ({self._call_count}/{self.rate_limit_per_minute} 次/分钟),"
                f"等待 {sleep_time:.1f} 秒..."
            )
            
            time.sleep(sleep_time)
            
            # 重置计数器
            self._minute_start = time.time()
            self._call_count = 0
        
        # 增加调用计数
        self._call_count += 1
        logger.debug(f"Tushare 当前分钟调用次数: {self._call_count}/{self.rate_limit_per_minute}")

    def _call_api_with_rate_limit(self, method_name: str, **kwargs) -> pd.DataFrame:
        """统一通过速率限制包装 Tushare API 调用。"""
        if self._api is None:
            raise DataFetchError("Tushare API 未初始化,请检查 Token 配置")

        self._check_rate_limit()
        method = getattr(self._api, method_name)
        return method(**kwargs)

    def _get_china_now(self) -> datetime:
        """返回上海时区当前时间,方便测试覆盖跨日刷新逻辑。"""
        return datetime.now(ZoneInfo("Asia/Shanghai"))

    def _get_trade_dates(self, end_date: Optional[str] = None) -> List[str]:
        """按自然日刷新交易日历缓存,避免服务跨日后继续复用旧日历。"""
        if self._api is None:
            return []

        china_now = self._get_china_now()
        requested_end_date = end_date or china_now.strftime("%Y%m%d")

        if self.date_list is not None and self._date_list_end == requested_end_date:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Set a valid TUSHARE_TOKEN in the runtime environment (and keep .env.example in sync).
  2. Gate TushareFetcher registration on token presence so unavailable providers are never selected.
  3. Catch DataFetchError at the manager and fall back to Akshare when Tushare is not configured.

Example fix

# before
df = tushare_fetcher.get_stock_data("600519", start, end)  # DataFetchError: not initialized

# after
# .env: TUSHARE_TOKEN=...
import os
if os.getenv("TUSHARE_TOKEN"):
    df = tushare_fetcher.get_stock_data("600519", start, end)
else:
    df = akshare_fetcher.get_stock_data("600519", start, end)
Defensive patterns

Strategy: validation

Validate before calling

import os

tushare_ready = bool(os.getenv("TUSHARE_TOKEN", "").strip()) and tushare_fetcher._api is not None
if not tushare_ready:
    df = akshare_fetcher.get_stock_data(code, start, end)

Try / catch

try:
    df = tushare_fetcher.get_stock_data(code, start, end)
except DataFetchError as e:
    if "未初始化" in str(e):
        df = akshare_fetcher.get_stock_data(code, start, end)
    else:
        raise

Prevention

When it happens

Trigger: Calling any Tushare data method (daily, trade_cal, ...) via _call_api_with_rate_limit when TUSHARE_TOKEN is unset/blank or initialization was skipped/failed during fetcher construction.

Common situations: Missing TUSHARE_TOKEN in .env for a new deployment; token defined only in local shell not in Docker/CI; fetcher instantiated before env is loaded; empty-string token passing a truthiness check elsewhere.

Related errors


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