{"record":{"id":"09a9d05664b4ee44","repo":"ZhuLinsen/daily_stock_analysis","slug":"akshare-e-09a9d0","errorCode":null,"errorMessage":"Akshare 获取港股数据失败: {e}","messagePattern":"Akshare 获取港股数据失败: (.+?)","errorType":"exception","errorClass":"DataFetchError","httpStatus":null,"severity":"error","filePath":"data_provider/akshare_fetcher.py","lineNumber":885,"sourceCode":"            if df is not None and not df.empty:\n                logger.info(f\"[API返回] ak.stock_hk_hist 成功: 返回 {len(df)} 行数据, 耗时 {api_elapsed:.2f}s\")\n                logger.info(f\"[API返回] 列名: {list(df.columns)}\")\n                logger.info(f\"[API返回] 日期范围: {df['日期'].iloc[0]} ~ {df['日期'].iloc[-1]}\")\n                logger.debug(f\"[API返回] 最新3条数据:\\n{df.tail(3).to_string()}\")\n            else:\n                logger.warning(f\"[API返回] ak.stock_hk_hist 返回空数据, 耗时 {api_elapsed:.2f}s\")\n            \n            return df\n            \n        except Exception as e:\n            error_msg = str(e).lower()\n            \n            # 检测反爬封禁\n            if any(keyword in error_msg for keyword in ['banned', 'blocked', '频率', 'rate', '限制']):\n                logger.warning(f\"检测到可能被封禁: {e}\")\n                raise RateLimitError(f\"Akshare 可能被限流: {e}\") from e\n            \n            raise DataFetchError(f\"Akshare 获取港股数据失败: {e}\") from e\n    \n    def _normalize_data(self, df: pd.DataFrame, stock_code: str) -> pd.DataFrame:\n        \"\"\"\n        标准化 Akshare 数据\n        \n        Akshare 返回的列名（中文）：\n        日期, 开盘, 收盘, 最高, 最低, 成交量, 成交额, 振幅, 涨跌幅, 涨跌额, 换手率\n        \n        需要映射到标准列名：\n        date, open, high, low, close, volume, amount, pct_chg\n        \"\"\"\n        df = df.copy()\n        \n        # 列名映射（Akshare 中文列名 -> 标准英文列名）\n        column_mapping = {\n            '日期': 'date',\n            '开盘': 'open',\n            '收盘': 'close',","sourceCodeStart":867,"sourceCodeEnd":903,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/akshare_fetcher.py#L867-L903","documentation":"The generic DataFetchError raised by AkshareFetcher._fetch_hk_data when ak.stock_hk_hist fails with any exception not classified as rate limiting. It wraps the original akshare exception (available via __cause__) and is the signal for DataFetcherManager to try the next HK data source in its priority chain.","triggerScenarios":"Calling fetch for an HK code ('00700') when stock_hk_hist raises: invalid symbol argument (e.g. '700' without zero padding), network timeout to East Money, JSON decode error on an HTML block page without keywords, or akshare interface changes after upgrade.","commonSituations":"HK codes passed without 5-digit zero-padding; akshare version drift changing stock_hk_hist parameters ('period'/'adjust' renamed); intermittent East Money 5xx errors.","solutions":["Check e.__cause__: TypeError usually means akshare API change → pin/upgrade akshare; JSONDecodeError → block page or endpoint change.","Normalize HK codes to 5 digits before calling (e.g. '00700', not '700').","Retry once after a short sleep for transient 5xx, then let fallback take over.","Confirm ak.stock_hk_hist works standalone in a REPL with the same arguments."],"exampleFix":"# before\nakshare_fetcher.fetch('700', start, end)  # HK code missing padding\n\n# after\ncode = '700'.zfill(5)  # '00700'\nakshare_fetcher.fetch(code, start, end)","handlingStrategy":"fallback","validationCode":"code = '700'.zfill(5)  # HK codes need 5-digit zero padding for ak.stock_hk_hist\nassert len(code) == 5 and code.isdigit()","typeGuard":"def is_valid_hk_code(code: str) -> bool:\n    c = code.lower().removeprefix('hk')\n    return len(c) == 5 and c.isdigit()","tryCatchPattern":"try:\n    df = akshare_fetcher.fetch(hk_code, start, end)\nexcept RateLimitError:\n    raise\nexcept DataFetchError as e:\n    logger.warning('akshare HK failed for %s: %s', hk_code, e)\n    df = manager.fetch(hk_code, start, end)","preventionTips":["Normalize HK codes to 5-digit padded form at config load.","Test ak.stock_hk_hist standalone after akshare upgrades.","Keep a second HK-capable source in the chain."],"tags":["akshare","hk-stocks","data-provider","fallback","stock-code"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}