ZhuLinsen/daily_stock_analysis · error · DataFetchError

PytdxFetcher 不支持北交所 {stock_code},将自动切换其他数据源

Error message

PytdxFetcher 不支持北交所 {stock_code},将自动切换其他数据源

What it means

DataFetchError raised in PytdxFetcher._fetch_raw_data when is_bse_code(stock_code) is true (Beijing Stock Exchange, 8xxxxx/4xxxxx codes). The free TDX endpoints used here lack BSE coverage, so the fetcher declines and relies on the manager to switch data sources.

Source

Thrown at data_provider/pytdx_fetcher.py:332

        使用 get_security_bars() 获取日线数据
        
        流程:
        1. 检查是否为美股(不支持)
        2. 使用上下文管理器管理连接
        3. 判断市场代码
        4. 调用 API 获取 K 线数据
        """
        # 美股不支持,抛出异常让 DataFetcherManager 切换到其他数据源
        if _is_us_code(stock_code):
            raise DataFetchError(f"PytdxFetcher 不支持美股 {stock_code},请使用 AkshareFetcher 或 YfinanceFetcher")

        # 港股不支持,抛出异常让 DataFetcherManager 切换到其他数据源
        if _is_hk_market(stock_code):
            raise DataFetchError(f"PytdxFetcher 不支持港股 {stock_code},请使用 AkshareFetcher")

        # 北交所不支持,抛出异常让 DataFetcherManager 切换到其他数据源
        if is_bse_code(stock_code):
            raise DataFetchError(
                f"PytdxFetcher 不支持北交所 {stock_code},将自动切换其他数据源"
            )
        
        market, code = self._get_market_code(stock_code)
        
        # 计算需要获取的交易日数量(估算)
        from datetime import datetime as dt
        start_dt = dt.strptime(start_date, '%Y-%m-%d')
        end_dt = dt.strptime(end_date, '%Y-%m-%d')
        days = (end_dt - start_dt).days
        count = min(max(days * 5 // 7 + 10, 30), 800)  # 估算交易日,最大 800 条
        
        logger.debug(f"调用 Pytdx get_security_bars(market={market}, code={code}, count={count})")
        
        with self._pytdx_session() as api:
            try:
                # 获取日 K 线数据
                # category: 9-日线, 0-5分钟, 1-15分钟, 2-30分钟, 3-1小时

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Put a BSE-capable provider (e.g. Akshare/Tushare) ahead of pytdx for BSE codes, or exclude pytdx from BSE routing.
  2. Pre-filter with is_bse_code at the dispatch layer.
  3. Confirm the replacement provider actually covers BSE daily bars before switching.

Example fix

# before
df = pytdx_fetcher.get_stock_data("832566", start, end)  # DataFetchError

# after
from src.utils.stock_utils import is_bse_code
provider = akshare_fetcher if is_bse_code(code) else pytdx_fetcher
df = provider.get_stock_data(code, start, end)
Defensive patterns

Strategy: type-guard

Validate before calling

from src.utils.stock_utils import is_bse_code

fetcher = akshare_fetcher if is_bse_code(stock_code) else pytdx_fetcher

Type guard

from src.utils.stock_utils import is_bse_code

def is_bse_symbol(code: str) -> bool:
    """True when the code is a Beijing Stock Exchange listing."""
    return is_bse_code(code)

Try / catch

try:
    df = pytdx_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: Requesting daily K-line history for a BSE-listed code (e.g. 83xxxx, 43xxxx, 92xxxx patterns matched by is_bse_code) from PytdxFetcher.

Common situations: Watchlists extended with 北交所 stocks while the provider chain still has pytdx as first A-share source; BSE IPOs added to scheduled analysis without provider coverage review.

Related errors


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