公募基金综合分析
一个技能覆盖原来四个技能的全部场景,四种模式共用一套数据与计算口径:
| 模式 | 干什么 | 典型说法 |
|---|---|---|
nav |
只抓净值,落盘备用 | “帮我把这几只基金的净值抓下来” |
single |
单只基金深度分析 | “分析一下易方达供给改革 002910” |
compare |
多只(≥2)横向对比 | “这几只基金哪个好,对比一下” |
portfolio |
按真实申赎流水做组合分析 | “我买的这个组合收益怎么样” |
输出统一为三件套:report.html(图片 base64 内联,微信可直接转发)、report.docx、report_long.png(整页长图)。
一、运行环境(先看这条)
# 必须是装了 numpy / pandas / matplotlib 的解释器;本机推荐:
C:\Users\HT\.workbuddy\binaries\python\envs\default\Scripts\python.exe
- 依赖:
numpy pandas matplotlib;DOCX 需要python-docx;整页长图需要本机 Chrome 或 Edge(走无头 CDP,无第三方包)。 - 若解释器缺依赖,
run.py会直接报“当前解释器缺少依赖:xxx”并给出 pip 命令,不会跑到一半崩。 - 全部数据源都是公开免密钥接口:净值来自天天基金
fund.eastmoney.com/pingzhongdata/{code}.js,指数来自东方财富 K 线push2his.eastmoney.com/api/qt/stock/kline/get。不需要任何 API Key。
二、一条命令跑完整条流水线
cd <本skill>/scripts
# 单只
python run.py --mode single --funds 002910 --dir D:\case\单只_002910
# 多只(≥2 只,多少只都行)
python run.py --mode compare --funds 005110 014591 002910 --dir D:\case\对比
# 组合(需要 config.json,见第五节)
python run.py --mode portfolio --config D:\case\组合\config.json --dir D:\case\组合
# 只抓净值
python run.py --mode nav --funds 005110 014591 --dir D:\case\数据
流程固定为:抓净值 → 基准指数增量同步 → 计算指标与出图 → HTML → DOCX → 图片内联 → 整页长图。
run.py 参数
| 参数 | 说明 |
|---|---|
--mode |
nav / single / compare / portfolio,必填 |
--funds |
基金代码,可多个;single 取第一个,compare 至少 2 个 |
--dir |
工作目录,缺省 ./fund_case_<时间戳> |
--config |
组合模式的 config.json 路径(缺省取 <dir>/config.json) |
--bench |
业绩基准,默认 中证800;也接受 csi300 / 沪深300 / 000905 等别名 |
--window-start |
single 模式分析窗口起点 YYYY-MM-DD |
--window-years |
single 模式窗口长度,默认 2 年 |
--skip-fetch |
跳过抓净值,复用已有 数据/(改图表/改版式时用) |
--no-docx / --no-longpng / --no-inline |
跳过对应产物 |
产物目录
<dir>/
├── report.html 自包含单文件(图片已 base64 内联)
├── report.docx
├── report_long.png 整页长图
├── 数据/ 净值 CSV、指数 CSV、metrics.json / compare.json / portfolio.json
└── 报告图片/ 全部 PNG
三、数据与计算口径(写报告时必须一致)
- 净值:天天基金
Data_netWorthTrend/equityReturn连乘得到后复权净值(含分红再投资,首日 = 1.0)。若某日equityReturn缺失,退回单位净值环比。分红日不能直接用单位净值算收益,否则会凭空出现一根大跌。 - 年化收益:
(1+累计收益)^(365/自然日天数) - 1;年化波动、跟踪误差、下行波动按 244 个交易日折算。 - 无风险利率:固定 2%,夏普/索提诺都用它。
- 配色:红涨绿跌(红
#C0392B= 正收益/上涨,绿#1E8449= 负收益/下跌)。波动率、回撤、胜率这类没有方向的指标不加正负号、不用红绿(用中性蓝/灰),避免误读。 - 多只对比:所有基金取共同交易日交集(inner join),用同一个基准算超额,报告首页写明「共同区间 X~Y,共 N 个交易日」——不同成立日的基金不能各算各的区间再比。
- 超额两种口径别混用:
excess.total_pct是比值口径(组合累计净值 ÷ 基准累计净值 − 1),annual_pct是其年化;报告文案里的「跑赢 X 个百分点」必须用两序列累计收益之差(pp),不能直接拿比值口径当百分点引用,比值口径只作为「相当于基准的 N 倍」补充说明。两种都已按此改好,改动时勿回退。 - 没有方向的指标不加正负号:波动率、下行波动率、胜率、夏普/卡玛/索提诺、盈亏比一律用
fnum(不加+),只有收益、超额、回撤、盈亏用fmt/sgn。混用会导致「年化波动率 +22.31%」「胜率 +50.00%」这类误读。 - 组合:时间加权(TWR)合成组合净值,火富牛口径:
-px_off = -1表示按买入日前一交易日净值确认;
-px_off = 0表示按当日净值确认(公募「15:00 前下单按 T 日净值确认」,报真实流水时首选 0,非交易日顺延至下一交易日);
- 赎回视为现金流出组合(不再计入组合资产),同时给出 IRR 口径的持有人收益率;
- 单只基金的pnl_pct会把已赎回落袋的钱算进去,因此可能远大于组合整体收益率——这是口径差异,不是错。 - 文案全部数据驱动:脚本里不写死任何基金名、金额、日期、结论,换一批基金报告自动跟着变。
四、三种分析模式各自看什么
4.1 single 单只深度分析
报告六节:核心结论 → 收益 → 风险与持有体验 → 超额与择时 → 风格与主题暴露 → 风险提示与口径。
- 多区间收益:近1月/3月/6月、今年以来、近1/2/3/5年、成立以来(含年化)。
- 风险:年化波动、下行波动、最大回撤、回撤修复天数、偏度峰度、日胜率、盈亏比、最长连跌/连涨、创新高频率、夏普/卡玛/索提诺。
- 超额(默认相对中证800):累计超额、年化超额、跟踪误差、信息比率、超额回撤。
- 择时:月度上下行捕获率;日度上行/下行 Beta;极端下跌检验——基准单日跌幅 ≤ -2% 时基金日均 vs 基准日均的损失比,并给出“是否控制在基准一半以内”的明确判断。
- 风格/主题回归:6 个风格因子(大/中/小盘 × 成长/价值)+ 3 个主题因子(国证红利、科创50、基准),做截面回归(全样本/近1年/近半年)与滚动 22 日回归,看是否风格漂移。
- 分析窗口默认近 2 年(
--window-start/--window-years可改),报告里会写明窗口,避免拿老经理的业绩评价新经理。
4.2 compare 多只横向对比
- 共同区间 + 同一基准,逐项并列:收益、风险、性价比、超额、择时、风格、主题。
- 综合评分(0~100,分位加权):年化收益 35% + 夏普 25% + 信息比率 20% + 极端日损失比 10% + 最大回撤 10%。各维度在参评基金内部算分位,某维度无方差或样本不足时不参与、权重重新归一(不会出现全部 50 分的假平局)。
- 给出「怎么选」的分场景建议(要收益弹性 / 要回撤小 / 要超额稳)。
- N 只都支持,2 只、3 只、10 只同一套逻辑;颜色按基金身份固定,同一只基金在净值图、回撤图、评分图里颜色一致。
4.3 portfolio 组合分析
- 按真实流水还原每一笔买入份额、赎回金额、现金余额;组合净值 = 各基金市值合计 / 期初投入(TWR)。
- 输出:组合累计与年化收益、波动、最大回撤与回撤区间、夏普、卡玛、月度胜率、盈亏比、IRR(资金加权)。
- 子基金贡献:期末市值占比、累计盈亏、对组合收益的贡献度;配置结构饼图(期初 / 期末)。
- 相关性矩阵 + 滚动相关性、策略分布、组合归一净值 vs 子基金、连续盈亏、年度收益、分年度月度热力图。
五、组合模式的 config.json
{
"portfolio_name": "我的基金组合",
"benchmark": "中证800",
"latest": "2026-10-30",
"funds": [
{"code": "005110", "name": "汇安多策略混合C", "short": "汇安多策略C",
"strategy": "均衡灵活配置", "company": "汇安基金"},
{"code": "002910", "name": "易方达供给改革混合", "short": "易方达供给改革",
"strategy": "价值蓝筹", "company": "易方达基金"}
],
"transactions": [
{"fund": "005110", "type": "buy", "date": "2024-03-11", "amount": 100000, "label": "买入10万", "px_off": -1},
{"fund": "005110", "type": "buy", "date": "2025-02-10", "amount": 50000, "label": "加仓5万", "px_off": 0},
{"fund": "002910", "type": "redeem", "date": "2026-06-26", "ratio": 0.5, "label": "赎回50%份额", "px_off": 0}
]
}
funds:组合里的全部基金;strategy/company只用于图表分类和文案,可留空。transactions:type只能是buy/redeem;buy用amount(申购金额,元),redeem用ratio(赎回份额比例 0~1,必填,本技能不支持按金额赎回——要按金额赎回请先换算成比例)。记真实流水时px_off建议统一填0(按申请日当日净值确认)。latest:组合估值截止日,缺省用最新净值日。data_source(可选):报告末尾「数据来源」那段文字,用了第三方数据源必须改,否则会写成天天基金。extra_notes(可选,字符串数组):基金/数据特有事项(数据断档、拼接、确认日顺延等),会自动追加到报告口径说明里。- 缺少 config 时
run.py会直接提示格式,不会静默跑错。
六、图表清单
single(13 张):fund_nav_vs_bench fund_rolling fund_drawdown fund_retdist fund_multi_period fund_annual fund_monthly_heat fund_timing_scatter fund_extreme fund_style_exposure fund_theme_exposure fund_style_rolling fund_theme_rolling
compare(10 张,前缀 c_):c_nav c_dd c_multi c_risk c_timing c_monthly c_rolling c_style c_theme c_rank
portfolio(14+ 张):chart_timeline chart_nav_dd chart_monthly chart_rolling chart_alloc_init chart_alloc_curr chart_strategy chart_corr chart_fund_norm chart_pnl_cont chart_annual chart_fund_monthly_<年>
全部中文标签、标题含关键参数(如 R²、样本天数);单文件运行不依赖 CDN。
七、常见问题
| 现象 | 原因 / 处理 |
|---|---|
ModuleNotFoundError: numpy |
用错解释器了。换 ...\envs\default\Scripts\python.exe,或按提示 pip 安装 |
基准指数日志出现 [RETH] ... 单日区间失败 |
东方财富偶发断连,脚本已自动改用近 60 天区间重试,属正常兜底,最终仍有 [OK] 即可 |
| 基准同步全失败 | 检查网络;基准缓存已内置 benchmark_cache/(2023-01 起),无网也能跑历史区间 |
| 净值曲线某天突然断崖 | 检查是否踩到分红日。本技能用后复权净值,正常分红不会跳;若真跳说明该日 equityReturn 缺失且退回了单位净值 |
| 多只对比起点比预期晚 | 正常:取共同交易日交集,最晚成立的那只决定起点 |
| DOCX 里某张图缺失 | 对应图表函数返回空(如滚动主题回归样本不足),DOCX 会跳过该图而不是报错 |
长图 report_long.png 生成失败 |
没检测到 Chrome/Edge。HTML 本身已生成,可用浏览器手动打印为长图 |
| 报告里基金名不对 | 名字来自天天基金返回的 fS_name;组合模式可用 config 里的 name/short 覆盖,对比模式可直连 compare_funds.py --names "名称1" "名称2" ...(位置与 --funds 一一对应) |
八、用外部数据源取数(FTShare 等)、本地落盘后再分析
用户要求「先通过 X 取数、存本地,再从中读」时,不要让 run.py 直接抓天天基金,改成下面两步:
# 1) 取数并落盘(换成自己的抓取脚本,产出的文件名必须与本技能一致)
python scripts/fetch_ftshare.py <工作目录>/数据
# 2) 复用本地数据出报告:逐个调子脚本(等价于 run.py --skip-fetch,但不会覆盖基准)
python <skill>/scripts/portfolio_analysis.py --config <wd>/config.json --data <wd>/数据 --img <wd>/报告图片
python <skill>/scripts/build_report.py --mode portfolio --dir <wd> --config <wd>/config.json --out report.html
python <skill>/scripts/build_docx.py --mode portfolio --dir <wd> --config <wd>/config.json --out report.docx
python <skill>/scripts/inline_images.py report.html <wd>
python <skill>/scripts/shot_full.py report.html report_long.png
数据契约(照抄即可)
| 文件 | 列 | 说明 |
|---|---|---|
数据/{code}_em_nav.csv |
date,unit_nav,adj_nav,ret |
date=YYYYMMDD;unit_nav 评估持仓市值,adj_nav 算收益;ret 日收益率(小数) |
数据/{指数中文名}.csv |
date,close |
组合/单只模式的基准与风格指数,文件名须与 config 的 benchmark 完全一致 |
数据/{code}_meta.json |
code,name,rows,start,end |
可选,便于核对 |
FTShare 具体接口(market.ft.tech/gateway,请求头 FTSHARE_API_KEY)
- 净值:
/api/v1/market/data/fund/fund-net-value?fund_code=&start_date=&end_date=&page=&page_size=(≤200/页,需翻页)。返回unit_nav / accumulated_nav / adjusted_unit_nav / adjustment_factor;同一天可能同时返回「正式披露值」和「预估值」,按publish_date == nav_date优先去重。adj_nav直接取adjusted_unit_nav,收益用其环比。 - 基础信息:
/api/v1/market/data/fund/fund-basicinfo(名称/管理人/类型/业绩基准/经理),用于填 config 的name、company。 - 指数日线:
/api/v1/market/data/index-candlesticks?symbol=000906.SH&interval_unit=Day&since_ts_millis=&until_ts_millis=,单次跨度 ≤12 个自然月,需按 11 个月切片循环;close为字符串。
两个坑
- FTShare 指数日线最早只到 2022-04-19。若组合起始更早,基准会缺一段,直接算会让「超额」变成两个不同起点的错位比较。补法:用东财公开行情补缺口(
sync_benchmarks.py --start YYYYMMDD --cache <临时目录>),两源在重叠交易日收盘价偏差仅 0.0001%~0.0002%,可安全拼接;拼接动作要写进报告的口径说明。 - 建仓期净值断档(如 2022 年初成立的产品按周披露)会让「按某日申购」顺延到下一有净值的交易日,报告要注明实际确认日。
口径说明里请把真实数据源写进 config 的 data_source 字段(默认文案是天天基金+东财,不改会写错来源),基金特有事项写进 extra_notes 数组,会自动追加到报告末尾。
九、目录与脚本
公募基金综合分析/
├── SKILL.md
├── benchmark_cache/ # 9 个指数 CSV 缓存(date,close),跨任务共享、增量更新
└── scripts/
├── run.py # 统一入口(推荐只用它)
├── common.py # 共享层:读数、指标、回归、配色、绘图工具
├── fetch_nav.py # 天天基金净值抓取
├── sync_benchmarks.py # 东方财富指数增量同步
├── fund_analyzer.py # 单只分析引擎
├── compare_funds.py # 多只对比引擎(N≥2)
├── portfolio_analysis.py # 组合引擎(TWR / IRR)
├── report_text.py # 三模式文案生成(全数据驱动)
├── build_report.py # HTML(三模式一套样式)
├── build_docx.py # DOCX spec
├── make_docx.py # DOCX 渲染
├── inline_images.py # 图片 base64 内联(微信可转发)
└── shot_full.py # 无头 Chrome CDP 整页长图
单独调试某一环时可直接调对应脚本(都支持 --help),但正常使用只需 run.py。
十、质量与合规
- 所有数字必须来自实际计算,禁止编造;方法有局限(如滚动回归短窗口共线性、共同区间截断)要在报告里如实说明。
- 结尾附免责声明:报告基于公开数据与历史净值,不构成投资建议。
- 定性信息(规模、持仓、持有人、基金经理履历、费率)不在本技能范围——本技能只做净值口径的定量分析。需要时可另用
ftshare-market-data技能取数后人工补写。