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
- Check the specific fetcher's _fetch_raw_data implementation for branches that return None and make them raise DataFetchError with the real cause instead.
- Inspect the logged line '[name] 开始获取 ... 日线数据' just before the raise to identify which fetcher and date range produced None.
- 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
- Never return None from _fetch_raw_data overrides — raise DataFetchError with the cause.
- In tests, assert fetcher overrides never hit the None branch with real provider payloads.
- Let DataFetcherManager own failover instead of calling single fetchers directly.
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
- {call_name} 调用超过 {wait_seconds:g}s,已放弃等待
- [{self.name}] {stock_code}: {error_reason}
- {market_label} {stock_code} 获取失败: {errors joined by newline}
- 所有数据源获取 {stock_code} 失败: {errors joined by newline}
- 大盘复盘未返回可持久化报告
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/679c5e62a7246edc.
Report an issue: GitHub.