"""AKShare 数据源(兜底/校验)。免费、无需 token。 默认不安装(依赖较重);如需启用:`uv add akshare`。 fetcher 在 Tushare 失败时会尝试本模块;未安装则该路径自动跳过。 """ from __future__ import annotations from datetime import datetime from ..domain import Bar from .symbols import plain_code def fetch_daily(code: str, start: str = "20200101", end: str | None = None, adjust: str = "qfq") -> list[Bar]: import akshare as ak # 延迟导入 end = end or datetime.now().strftime("%Y%m%d") symbol = plain_code(code) adj_map = {"qfq": "qfq", "hfq": "hfq", "": "", None: ""} df = ak.stock_zh_a_hist( symbol=symbol, period="daily", start_date=start, end_date=end, adjust=adj_map.get(adjust, ""), ) if df is None or df.empty: raise RuntimeError(f"AKShare 无数据: {symbol}") bars: list[Bar] = [] for _, r in df.iterrows(): amt = r.get("成交额") bars.append( Bar( ts=datetime.strptime(str(r["日期"]), "%Y-%m-%d"), open=float(r["开盘"]), high=float(r["最高"]), low=float(r["最低"]), close=float(r["收盘"]), volume=float(r["成交量"]) * 100.0, # AKShare 成交量单位为手 -> 股 amount=float(amt) if amt is not None and amt == amt else None, # AKShare 成交额单位为元 ) ) return bars