{"record":{"id":"a3da4780d1761b65","repo":"ZhuLinsen/daily_stock_analysis","slug":"tushare-e-a3da47","errorCode":null,"errorMessage":"Tushare 获取数据失败: {e}","messagePattern":"Tushare 获取数据失败: (.+?)","errorType":"exception","errorClass":"DataFetchError","httpStatus":null,"severity":"error","filePath":"data_provider/tushare_fetcher.py","lineNumber":546,"sourceCode":"            else:\n                # Regular A-share stocks use daily interface\n                df = self._api.daily(\n                    ts_code=ts_code,\n                    start_date=ts_start,\n                    end_date=ts_end,\n                )\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 ['quota', '配额', 'limit', '权限']):\n                logger.warning(f\"Tushare 配额可能超限: {e}\")\n                raise RateLimitError(f\"Tushare 配额超限: {e}\") from e\n            \n            raise DataFetchError(f\"Tushare 获取数据失败: {e}\") from e\n    \n    def _normalize_data(self, df: pd.DataFrame, stock_code: str) -> pd.DataFrame:\n        \"\"\"\n        标准化 Tushare 数据\n        \n        Tushare daily / fund_daily 返回的列名：\n        ts_code, trade_date, open, high, low, close, pre_close, change, pct_chg, vol, amount\n        \n        需要映射到标准列名：\n        date, open, high, low, close, volume, amount, pct_chg\n\n        单位缩放仅适用于 A 股（及 ETF 等使用同一套单位的接口）：\n        - vol 按「手」计，乘以 100 转为「股」\n        - amount 按「千元」计，乘以 1000 转为「元」\n\n        港股 hk_daily 返回的 vol / amount 已是可直接使用的量级，不做上述缩放。\n        \"\"\"\n        df = df.copy()","sourceCodeStart":528,"sourceCodeEnd":564,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/tushare_fetcher.py#L528-L564","documentation":"The catch-all DataFetchError for TushareFetcher: any exception from the pro API call that does NOT match the quota keywords ('quota'/'配额'/'limit'/'权限') is wrapped here. Typical roots are network failures to api.tushare.pro, invalid/expired token rejected at call time, unsupported ts_code after conversion, or interface-argument errors (bad start/end date).","triggerScenarios":"POST to api.tushare.pro timing out or DNS-failing; token invalidated after construction; _convert_stock_code producing a ts_code Tushare rejects; passing dates where ts_start > ts_end or in wrong format; HK code routed to hk_daily without permission (message lacking quota keywords).","commonSituations":"Running in CI/scheduled Actions with restricted egress; token rotated but process holds old config; watchlist containing codes (e.g. odd ETF prefixes) the converter mishandles; weekends/holidays yielding empty ranges that some interfaces reject.","solutions":["Read the chained cause (raise ... from e) — the original exception text in {e} identifies network vs auth vs argument errors.","Verify the converted ts_code: python -c on _convert_stock_code for the failing symbol and test it on tushare.pro's online console.","Check connectivity/egress to api.tushare.pro and retry transient network failures.","If persistent, let the fallback chain serve the symbol from Akshare/Yfinance and file the ts_code issue with the converter."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"import socket\n\n# cheap reachability check before a batch run\ntry:\n    socket.create_connection((\"api.tushare.pro\", 443), timeout=5).close()\n    reachable = True\nexcept OSError:\n    reachable = False","typeGuard":null,"tryCatchPattern":"try:\n    df = tushare_fetcher.fetch_stock_data(code)\nexcept DataFetchError as e:\n    logger.warning(\"Tushare failed for %s: %s; falling back\", code, e)\n    df = akshare_fetcher.fetch_stock_data(code)  # or yfinance, per market","preventionTips":["Always chain data sources; a single-source failure must not abort the analysis run (repo guardrail).","Log the wrapped cause chain (e.__cause__) — the real reason is in the original exception.","Smoke-test ts_code conversion for new symbol formats before batch runs."],"tags":["tushare","network","data-provider","wrap"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}