ZhuLinsen/daily_stock_analysis · error · DataFetchError

[{self.name}] 未获取到 {stock_code} 的数据

Error message

[{self.name}] 未获取到 {stock_code} 的数据

What it means

Raised by BaseFetcher.get_daily_data when the concrete fetcher's _fetch_raw_data returns None instead of a DataFrame. The base class treats None as 'provider produced nothing' and immediately converts it into DataFetchError so the DataFetcherManager failover chain can try the next source.

Source

Thrown at data_provider/base.py:494

        # 计算日期范围
        if end_date is None:
            end_date = datetime.now().strftime('%Y-%m-%d')
        
        if start_date is None:
            # 默认获取最近 30 个交易日(按日历日估算,多取一些)
            from datetime import timedelta
            start_dt = datetime.strptime(end_date, '%Y-%m-%d') - timedelta(days=days * 2)
            start_date = start_dt.strftime('%Y-%m-%d')

        request_start = time.time()
        logger.info(f"[{self.name}] 开始获取 {stock_code} 日线数据: 范围={start_date} ~ {end_date}")
        
        try:
            # Step 1: 获取原始数据
            raw_df = self._fetch_raw_data(stock_code, start_date, end_date)
            
            if raw_df is None:
                raise DataFetchError(f"[{self.name}] 未获取到 {stock_code} 的数据")
            if raw_df.empty:
                elapsed = time.time() - request_start
                logger.info(
                    f"[{self.name}] {stock_code} 返回空日线结果: 范围={start_date} ~ {end_date}, "
                    f"elapsed={elapsed:.2f}s"
                )
                if self.allow_empty_daily_data:
                    return pd.DataFrame(columns=STANDARD_COLUMNS)
                raise DataFetchError(f"[{self.name}] 未获取到 {stock_code} 的数据")
            
            # Step 2: 标准化列名
            df = self._normalize_data(raw_df, stock_code)
            
            # Step 3: 数据清洗
            df = self._clean_data(df)
            
            # Step 4: 计算技术指标
            df = self._calculate_indicators(df)

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Check the specific fetcher's _fetch_raw_data implementation for branches that return None and make them raise DataFetchError with the real cause instead.
  2. Inspect the logged line '[name] 开始获取 ... 日线数据' just before the raise to identify which fetcher and date range produced None.
  3. If the provider legitimately has no data, return an empty DataFrame so the allow_empty_daily_data path (base.py:501) applies instead of None.

Example fix

// before (in a fetcher)
except Exception:
    return None
// after
except Exception as e:
    raise DataFetchError(f"[{self.name}] upstream failed: {e}") from e
Defensive patterns

Strategy: validation

Validate before calling

raw = fetcher._fetch_raw_data(code, start, end)
if raw is None:
    raise DataFetchError(f'[{fetcher.name}] returned None for {code}; fix fetcher')
df = fetcher.get_daily_data(code, ...)

Try / catch

try:
    df = fetcher.get_daily_data(code, ...)
except DataFetchError as e:
    log_and_failover(e)  # manager handles; direct callers should move to next source

Prevention

When it happens

Trigger: Calling get_daily_data(stock_code, days/end_date) on any fetcher subclass whose _fetch_raw_data hits a code path that returns None (e.g. a provider response parsed to nothing, a guarded except that swallows the error and returns None) instead of raising.

Common situations: Upstream API changed response shape so parsing silently yields None; a fetcher override added a defensive 'return None' branch; monkeypatched/mocked _fetch_raw_data in tests returning None.

Related errors


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