Skip to main content

fund-quant-backtest

A 股指数/基金估值定投与择时策略的可运行回测脚手架。

소스 정보

저장소
digoal/skills
최근 소스 활동
2026년 8월 27일 08:28
감지된 SKILL.md 언어
중국어
스타
41
포크
10

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
5 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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 股指数/基金上验证估值定投 / 择时类策略。核心交付物是: 1. 一份脚本 `/root/new/tmp/<task>/<date>/backtest_*.py`, 跑出结论 + CSV 2. 一份策略汇总报告(可在终端打印,也可写到 .md) 3. 可视化图表(可选, 但推荐: 让主人一眼看懂) 输入: 指数或基金代码 + 回测区间 + 策略档位(分位阈值 + 倍率) + 基础定投金额。 输出: 各策略的总投入 / 总份额 / 终值 / 年化 / 买入月数 / 最大回撤 + 关键时间点的决策明细。 **触发词**: 回测、定投算法、估值定投、智能定投、年化对比、分位算法、复现一遍。 **明确边界**: 本 skill 不负责以下任务, 请跳转其他 skill: - 投教类长文产出 → `retail-trading-manual-writer` - 短期 T+1 走势预测 → `a-stock-prediction` / `us-stock-prediction` - 个股财报深度分析 → `financial-report-analysis` - 估值因子与宏观经济体系讲透 → `industry-deep-explainer` ## Default Workflow 1. **先抓指数, 不抓基金净值。** - A 股宽基指数的历史收盘价 + 滚动 PE 在 akshare 中证接口一次拉齐: ```python ak.stock_zh_index_hist_csindex(symbol="H00300", start_date="...", end_date="...") ``` 输出列含 `收盘` 和 `滚动市盈率`, **无需二次拼接 PE 数据**。 - 其他指数的中证代码: - 沪深300 → `H00300` - 中证500 → `H00905` - 中证红利 → `H00922` - 上证50 → `H00016` - 创业板指 → `H03900` - 科创50 → `H02021` - 申万行业指数 / 港股指数 PE 通常需要别的源(乌龟量化 / 万得), 不要硬拉 akshare 这条接口。 2. **不要用 PE 静态版(乐咕乐股)** —— 接口挂了 csrf, 直接放弃。 3. **不要拉基金净值代替指数。** 净值有跟踪误差 + 管理费 + 分红再投近似问题, 用指数回测结论更干净。**最后再**用真实净值做交叉验证(可选)。 4. **滚动分位的标准做法**: ```python WIN = 1252 # 5 年 ~ 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 天决策。 5. **每月决策 = 每月第一个交易日**: ```python df['ym'] = df['日期'].dt.to_period('M') month_first = df.groupby('ym').first().reset_index() ``` 6. **回测循环核心(伪代码)**: ```python for s in ['s1_naive', 's2_zhifubao', 's3_v1', 's4_v2']: month_first[f'{s}_amt'] = month_first[s] * BASE_AMT month_first[f'{s}_share'] = month_first[f'{s}_amt'] / month_first['close'] total_invest = month_first[f'{s}_amt'].sum() total_shares = month_first[f'{s}_share'].sum() final_value = total_shares * df['close'].iloc[-1] cagr = (final_value / total_invest) ** (1/years) - 1 if total_invest > 0 else 0 ``` 7. **必须输出的指标**(表格): - 总投入、总份额、终值、终值收益额 - 年化收益(CAGR) - 买入月数 / 总月数(验证策略是否触发) - 最大回撤(逐月市值) 8. **必须输出与一次性买入对比** —— 提醒主人"起点敏感性问题"。 9. **回测起点很重要**: - 不要从牛市顶点(2015-06, 2021-02)起, 那会反过来证明定投更好, 结论不普适。 - 不要从最低点起, 那是上帝视角, 不能复现。 - 推荐从整段牛熊都覆盖的窗口起, 例如 2014-01 ~ 2024-12。 ## 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`, 例: ```python 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 实战沉淀 实战中主人明确表达的偏好, 后续回测必须满足, 不要替主人切档位: 1. **结果汇报的硬指标** (任一回测必须输出): - 累计投入、累计份额、终值、收益额 (不要只给 CAGR) - CAGR、买入月数 / 总月数 (验证策略是否触发) - 必要时给一次性买入作为起点敏感性参考, **但分两栏列, 不要混着看** 2. **分档偏好**: 30-50% 区间 0.5x, **50-70% 直接跳过(0)**。主人原话: "调整一项, 中性区间(分位 50-70%) 跳过不买. 30~50% 0.5倍"。这是有意设计的更严纪律, 不是疏漏。 3. **结论偏好**: 主人关注"绝对收益(终值)"远高于"年化"。回测总结话术要强调"投了 X 万, 终值 Y 万, 资金利用率 Z%", 不要只喊 CAGR。 4. **报告节奏**: 结论先行, 一张总表 + 3-5 条犀利观察, 不堆理论。详细数据落 CSV, 让主人自己复盘。 5. **回测分段**: 默认 2014-01 ~ 2024-12 覆盖完整牛熊, 但**额外加 2014-2018 / 2019-2024 拆分**, 让主人看清"近期是否仍适用"。 6. **设计先于自动化 (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 修改的回测模板。
GitHub에서 보기