"""量化练习公共库:数据获取(带缓存+重试) + 常用指标 + 回测指标。""" import os, time, pandas as pd, numpy as np CACHE = os.path.join(os.path.dirname(__file__), "..", "data", "cache") os.makedirs(CACHE, exist_ok=True) def _fetch(symbol, start, end, adjust, retries=6, wait=2.0): import akshare as ak last = None for i in range(retries): try: return ak.stock_zh_a_hist(symbol=symbol, period="daily", start_date=start, end_date=end, adjust=adjust) except Exception as e: # 网络间歇性抽风,重试 last = e time.sleep(wait * (i + 1)) raise last def get_daily(symbol="000001", start="20200101", end="20241231", adjust="qfq"): """拉单只A股日线(前复权),带本地缓存与自动重试。""" f = os.path.join(CACHE, f"{symbol}_{start}_{end}_{adjust}.csv") if os.path.exists(f): return pd.read_csv(f, parse_dates=["日期"]) df = _fetch(symbol, start, end, adjust) df.to_csv(f, index=False, encoding="utf-8-sig") return df def add_returns(df, price_col="收盘"): df = df.copy() df["ret"] = df[price_col].pct_change().fillna(0.0) return df def net_value(ret): return (1 + pd.Series(ret).fillna(0.0)).cumprod() def stats(ret, periods=252): ret = pd.Series(ret).fillna(0.0) nv = net_value(ret); total = nv.iloc[-1] - 1 ann = (1 + total) ** (periods / max(len(ret), 1)) - 1 vol = ret.std() * np.sqrt(periods) sharpe = (ret.mean() * periods) / vol if vol > 0 else np.nan dd = (nv / nv.cummax() - 1).min() calmar = ann / abs(dd) if dd < 0 else np.nan return {"总收益": f"{total:.2%}", "年化": f"{ann:.2%}", "年化波动": f"{vol:.2%}", "夏普": f"{sharpe:.2f}", "最大回撤": f"{dd:.2%}", "卡玛": f"{calmar:.2f}"} def max_drawdown(nv): return (nv / nv.cummax() - 1).min()