| name | fund-quant-backtest |
| description | A 股指数/基金估值定投与择时策略的可运行回测脚手架。 |
| version | 0.1.0 |
| author | Hermes Agent (curator-managed) |
| license | MIT |
| metadata | {"hermes":{"tags":["finance","backtest","a-stock","dca","valuation"],"related_skills":["retail-trading-manual-writer","a-stock-prediction","finance-core-analysis"]}} |
Fund Quant Backtest
When to Use
触发词 (任一即用): 回测、定投算法、估值定投、智能定投、年化对比、分位算法、复现一遍、对比普通定投。
典型场景:
- 主人在某个指数 / 基金上想试一种估值定投策略, 问"这个比普通定投好吗"
- 主人改了档位参数 (比如"30-50% 不买"), 需要在新参数下出回测结论
- 主人质疑支付宝 / 天天基金的"智能定投" 黑盒, 需要自有数据复现
- 主人问"X 指数 PE 现在几分位", 想做当下决策但顺便跑过去 10 年验证
不要用本 skill:
- 投教文章 / 长文产出 →
retail-trading-manual-writer
- 短期 T+1 涨跌预测 →
a-stock-prediction / us-stock-prediction
- 个股财报深度分析 →
financial-report-analysis
- 行业体系讲透 →
industry-deep-explainer
Class Contract
本 skill 提供 可运行的 Python 回测脚手架,用于在 A 股指数/基金上验证估值定投 / 择时类策略。核心交付物是:
- 一份脚本
/root/new/tmp/<task>/<date>/backtest_*.py, 跑出结论 + CSV
- 一份策略汇总报告(可在终端打印,也可写到 .md)
- 可视化图表(可选, 但推荐: 让主人一眼看懂)
输入: 指数或基金代码 + 回测区间 + 策略档位(分位阈值 + 倍率) + 基础定投金额。
输出: 各策略的总投入 / 总份额 / 终值 / 年化 / 买入月数 / 最大回撤 + 关键时间点的决策明细。
触发词: 回测、定投算法、估值定投、智能定投、年化对比、分位算法、复现一遍。
明确边界: 本 skill 不负责以下任务, 请跳转其他 skill:
- 投教类长文产出 →
retail-trading-manual-writer
- 短期 T+1 走势预测 →
a-stock-prediction / us-stock-prediction
- 个股财报深度分析 →
financial-report-analysis
- 估值因子与宏观经济体系讲透 →
industry-deep-explainer
Default Workflow
- 先抓指数, 不抓基金净值。
- 不要用 PE 静态版(乐咕乐股) —— 接口挂了 csrf, 直接放弃。
- 不要拉基金净值代替指数。 净值有跟踪误差 + 管理费 + 分红再投近似问题, 用指数回测结论更干净。最后再用真实净值做交叉验证(可选)。
- 滚动分位的标准做法:
WIN = 1252
df['pe_pct'] = df['pe_ttm'].rolling(WIN, min_periods=252).apply(
lambda x: np.sum(x[:-1] <= x[-1]) / len(x[:-1]),
raw=True
)
- 窗口选择: 5 年跨 1-2 个完整牛熊, 7-8 年更平滑, 10 年反应滞后。与主人确认。
- 头 252 交易日分位用全历史填充, 但只用于显示, 不用于前 252 天决策。
- 每月决策 = 每月第一个交易日:
df['ym'] = df['日期'].dt.to_period('M')
month_first = df.groupby('ym').first().reset_index()
- 回测循环核心(伪代码):
for s in ['s1_naive', 's2_zhifubao', 's3_v1', 's4_v2']:
month_first[] = month_first[s] * BASE_AMT
month_first[] = month_first[] / month_first[]
total_invest = month_first[].()
total_shares = month_first[].()
final_value = total_shares * df[].iloc[-]
cagr = (final_value / total_invest) ** (/years) - total_invest >
Standard Strategy Ladder
默认提供的策略档位(可改):
| 策略名 | 触发条件 | 扣款 |
|---|
s1_naive | 不论分位 | 1.0x |
s2_zhifubao | PE 分位 < 30% | 1.0x, 否则 0 |
s3_v1_middle_buy | 三指标任一触发低估 | 1.5x, 30-70% 区间 0.5x, > 70% 0 |
s4_v2_final | < 30% → 1.5x, 30-50% → 0.5x, > 50% → 0 | (主人最终采纳版, 单 PE 信号) |
s5_v4_three_indicators | PE 分位 + 股债比 + 60月均线趋势 | 1.5x / 0.5x / 0 / 极低估 3x + 趋势折扣 (主人首选, 三指标版) |
s6_extreme | PE 分位 < 15% AND PB 分位 < 15% | 3.0x |
两版并存的关系:
s4_v2_final: 单 PE 信号 + 主人档位偏好。简单, 透明度最高, 主人最初拍板的版本。
s5_v4_three_indicators: PE 分位 + 股债比 (1/PE / 10Y 国债) + 60月均线趋势过滤。这是 2026-08-27 主人进一步要求的版本, 也是回测下来"绝对收益 + 资金利用率"双优的版本。默认推荐用这个。
回测验证 (沪深300, 2014-2026-08, 152 个月, 投入 1000/月):
| 策略 | 终值 | CAGR | 买入月数 |
|---|
| 普通定投 | 220,452 | 2.98% | 152/152 |
| 支付宝风 (PE<30%才买) | 147,660 | 4.08% | 89/152 |
| v2 简化 (PE分位单指标) | 181,009 | 3.72% | 110/152 |
| v4 三指标 + 趋势 | 207,698 | 3.51% | 111/152 |
沪深300 全收益指数同期 2602 → 6799, 涨幅 161.3% (年化 7.9%)。
v4 不是 CAGR 最高, 是绝对收益最高 + 资金利用率最高 —— 这正是主人的偏好: 总收益才是真收益, 年化是给营销看的。后续新对话如果主人再调, 务必 patch 这个表, 不要在脚本里悄悄改。
分段表现 (2026-08-27 主人额外要求)
默认 2014-2024 跑完一轮后, 主人会再问"扩到 2026 看近期" —— 直接加分段表:
| 段 | 时长 | 普通定投 CAGR | v4 CAGR | v4 优势 |
|---|
| 慢牛 (2019-2020) | 24 月 | 14.57% | 20.37% | +5.8 pp |
| 阴跌 (2021-2023) | 36 月 | -5.90% | -2.84% | +3.1 pp |
| 反弹 (2024-2026) | 32 月 | 6.33% | 12.38% | +6.0 pp |
核心洞察: v4 在三种不同行情下都跑赢普通定投, 阴跌段损失减半、反弹段收益翻倍。这是估值信号的真正价值, 不是只看 CAGR 能看出来的。后续回答主人"为什么用 v4"时, 直接引用这段话。
最近 12 个月 (2025-09 ~ 2026-08) v4 决策 = 全部 0.0 (PE 分位 73-96%) —— 这就是 v4 在贵市里的"沉默期", 用来回答"算法是不是死了"质疑的最佳证据。
Common Pitfalls
- ❌ 用
df['pe'].rolling().rank(pct=True) —— 那给的是滚动百分位, 不是历史分位, 含义不同。
- ❌ 在回测里"用未来数据算分位"(rolling 包含当日)—— 必须
x[:-1] <= x[-1], 防止前视偏差。
- ❌ 用"全历史百分位"代替滚动窗口 —— 会让分位永远不进入"低估区", 永无扣款, 假阴性。
- ❌ 把"一次性买入"和"定投"放同一张图比较终值 —— 起点偏差巨大。要么同起点比较, 要么标注 "假设你某日开始"。
- ❌ 把 akshare 拉到的"沪深300全收益指数"当成价格指数 —— 它含分红再投, 比真实价格指数高, 做严格回测要减分红或换口径。
- ❌ 代理不稳时(
Cannot connect to proxy)—— 用 curl --socks5-hostname host.docker.internal:11112, 或重试一次, 不要无限重试。
Visualization
回测结论最好落地为一张图: X 轴时间, Y 轴累计投入 / 累计市值, 多策略叠加。
推荐 matplotlib + fill_between, 例:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 6))
for s in ['s1', 's2', 's3', 's4']:
cum_value = (month_first[f'{s}_share'].cumsum() * month_first['close']).values
ax.plot(month_first['日期'], cum_value, label=s)
ax.legend()
ax.grid(alpha=0.3)
图存到 /root/new/tmp/<task>/<date>/fig_*.png, 在对话里直接引用路径。
主人偏好 (digoal/德哥) —— 2026-08-27 实战沉淀
实战中主人明确表达的偏好, 后续回测必须满足, 不要替主人切档位:
- 结果汇报的硬指标 (任一回测必须输出):
- 累计投入、累计份额、终值、收益额 (不要只给 CAGR)
- CAGR、买入月数 / 总月数 (验证策略是否触发)
- 必要时给一次性买入作为起点敏感性参考, 但分两栏列, 不要混着看
- 分档偏好: 30-50% 区间 0.5x, 50-70% 直接跳过(0)。主人原话: "调整一项, 中性区间(分位 50-70%) 跳过不买. 30~50% 0.5倍"。这是有意设计的更严纪律, 不是疏漏。
- 结论偏好: 主人关注"绝对收益(终值)"远高于"年化"。回测总结话术要强调"投了 X 万, 终值 Y 万, 资金利用率 Z%", 不要只喊 CAGR。
- 报告节奏: 结论先行, 一张总表 + 3-5 条犀利观察, 不堆理论。详细数据落 CSV, 让主人自己复盘。
- 回测分段: 默认 2014-01 ~ 2024-12 覆盖完整牛熊, 但额外加 2014-2018 / 2019-2024 拆分, 让主人看清"近期是否仍适用"。
- 设计先于自动化 (2026-08-27 主人明确纠正):
- 主人原话: "先别急cron, 你给个比较优的算法先."
- 触发模式: 主人提出"我有个策略 / 我在用 X, 帮我评估 / 落 cron"时, 不要第一时间就搭 cron。先用 1-2 轮对话设计/论证/调优算法, 等主人明确说"落 cron"才进入部署阶段。
- 判断信号: 主人没说"现在就建" / "落 cron" / "跑起来" 时, 默认不要进入 cron/job/state script 阶段。
- 错踩后果: 上来就建 cron 会被主人打回, 浪费一轮; 上来就回答"好, 落 cron"是替主人切档位, 违反主人"先设计后部署"的偏好。
已知数据源陷阱 (实战踩坑沉淀)
ak.stock_zh_index_value_csindex (中证指数估值) 返回的 xls URL (oss-ch.csindex.com.cn/static/html/...) 已 404, 实际是占位 JPEG 图片。不要用这个接口拿历史 PE/PB/股息率。直接走 stock_zh_index_hist_csindex。
- 10 年国债日频: 用
ak.bond_zh_us_rate(), 含 1990-2026 全历史, 列名 中国国债收益率10年。akshare 的 bond_china_yield() 只覆盖到 2021, 别用。
- 乐咕乐股 PE 静态版 (
ak.stock_index_pe_lg): 挂了 csrf, 直接放弃, 别尝试。雪球 PE 也有 csrf / 反爬, 不推荐。
从回测到生产: cron 部署阶段 (2026-08-27 主人拍板落地)
回测结论跑通 + 主人明确说"落 cron"后, 进入此阶段。默认推荐用 --script + --no-agent 纯脚本模式, 不是 agentic 模式:
- daily-decision 是 if-then-else 表格, 不需要 LLM 阅读理解
- 节省 token, 跑得快 (< 5s)
- 用
hermes-cron-orchestrator 描述的 agentic 模式是浪费 (那 skill 主要服务"数据 + LLM 解读"型 cron)
完整模板 / 部署流程 / 时区换算 / 7 步走 / 踩坑速查表, 看 references/production-deployment.md。关键提醒:
- 脚本必须放
/root/.hermes/profiles/digoal/scripts/ (per-profile, 不是全局)
--script 参数用纯文件名 (不要绝对路径)
- workdir 必须在
/root/new/work/digoal/ 下
- exit code 透传给 cron, 失败不要静默
Reference Files
references/akshare-pe-data-sources.md: 中证 / 乐咕乐股 / 雪球 / 万得 各源对比, 哪个能用、哪个有 csrf、字段含义。
references/valuation-strategy-design-notes.md: 估值定投档位选择背后的依据, 三指标 / 趋势过滤 / EPS 一致预期修正等高级条件的取舍逻辑。
references/production-deployment.md: 回测结论跑通后的生产化部署模式 (hermes cron + script + no-agent), 包括 wrapper.sh 模板、workdir 布局、history.json 格式、常见踩坑。
scripts/dca_backtest_template.py: 一个可直接 copy 修改的回测模板。