BARRA组合风格归因与风险预测
下载地址
https://www.zhengdianxue.com/wp-content/uploads/skills/zerone-iteration--barra-portfolio-xray-lab.zip将提示词发送给你的 AI 安装该 skills
请根据 https://skillhub.cn/install/skillhub.md,安装 @zerone-iteration/barra-portfolio-xray-lab。给任意持仓做两次扫描:一次把收益拆成市场、行业与 9 类风格因子的贡献并输出暴露百分位,一次用结构化协方差矩阵预测未来波动并用偏差统计量检验模型可信度;可选输出 long-only GMV 最小方差权重。仅统计研究,不构成投资建议。
阅读全文 (展开查看完整说明)
概述
持仓CT机
Pay Skill 范式:鉴权型(agent_local)
鉴权型:履约在本机执行(需本地取数/解析大文件),远端网关只做支付鉴权,支付通过后返回
fulfillment: "agent_local",Agent 再跑scripts/下的脚本产出结果。
Overview
把 WorkBuddy 变成一台持仓 CT 机:给任意持仓做两次扫描,回答两个问题——收益从哪来(风格归因)与风险有多大(风险预测)。
- 第一次扫描(收益归因):用带约束条件的加权最小二乘(WLS)对截面收益做回归,把市场因子(截距项)、行业因子与 9 类风格因子(Beta / 规模 / 估值 / 成长 / 流动性 / 长期动量 / 短期动量 / 波动率 / 非线性规模)拆开,输出纯净因子收益与组合在各因子上的暴露百分位;再把给定组合的收益逐项拆成各因子贡献与残余 alpha。
- 第二次扫描(风险预测):用结构化方法估计协方差矩阵
V = X·F·Xᵀ + Δ——因子协方差 F 依次走 EWMA(半衰期 90)→ Newey-West 自相关调整(滞后期 2)→ 特征值调整(蒙特卡洛去偏,调整系数 1.2)→ 波动率偏误调整(半衰期 42);特异风险 Δ 依次走 EWMA(半衰期 90)→ Newey-West(滞后期 5)→ 结构化模型调整(协调参数 γ、去偏常数 1.05)→ 贝叶斯收缩(压缩系数 1)→ 波动率偏误调整。由此预测组合未来 21 日波动,并用偏差统计量检验"算得准不准",最后可选输出 long-only GMV 最小方差权重与基准对比。
覆盖范围:给定因子面板(date, ticker, ret, mktcap, 可选 industry/turnover/pe/pb/ps)与持仓权重,输出归因表、暴露百分位、波动预测、偏差检验、GMV 权重五类结果。不做什么:不荐股、不给目标价、不预测涨跌方向、不访问任何外部行情接口(数据完全由用户提供的 CSV 传入)、不承诺任何收益。
When to Use(触发条件)
- 用户说「我今年赚的钱到底是哪来的?是选股能力还是风格暴露?」
- 用户说「我的组合在小市值 / 高 Beta 上押了多少?给我看暴露百分位」
- 用户说「我这个组合下个月波动大概多少?会不会失控?」
- 用户说「我的风险模型算出来的波动跟实际差多少?模型到底可不可信?」
- 用户说「成分股不变,有没有比我现在的权重更稳的组合?」(GMV 最小方差)
- 用户说「帮我做一次多因子归因 / Brinson 之外的风格归因 / 风险归因」
First-Use Configuration(首次使用时收集)
| 参数 | 配置项 | 默认值 | 说明 |
|---|---|---|---|
| 预测窗口 | risk.horizon_days | 21 | 风险预测的交易日长度(1 个月) |
| 因子协方差半衰期 | risk.tau_sigma | 90 | EWMA 方差/协方差半衰期(交易日) |
| 因子协方差样本长度 | risk.h | 252 | EWMA 回看窗口(交易日) |
| NW 滞后期(因子) | risk.nw_lag_factor | 2 | 因子协方差 Newey-West 滞后期 |
| NW 滞后期(特异) | risk.nw_lag_specific | 5 | 特异风险 Newey-West 滞后期 |
| 特征值调整模拟次数 | risk.mc_sims | 500 | 蒙特卡洛次数(默认取小值以控耗时,参数可调至 10000) |
| 特征值调整系数 | risk.eigen_a | 1.2 | 经验风险偏差放大系数 a |
| 结构化模型去偏常数 | risk.e0 | 1.05 | 消除残差指数次幂偏误 |
| 贝叶斯收缩系数 | risk.shrink_q | 1 | 压缩系数 q |
| VRA 半衰期 | risk.tau_vra | 42 | 波动率乘数半衰期 |
| 归因显著性阈值 | attrib.t_threshold | 2.0 | |t| 超过即判定当期显著 |
| 暴露百分位中性带 | exposure.neutral_band | 10 | 50%±该值视为中性暴露 |
| 订阅渠道 | subscription.channel | (空) | 非空时横幅显示「您已通过 XXX 订阅此技能」 |
Workflow(工作流)
- 确认输入面板:读
references/rules.md「输入契约」一节。必须列date,ticker,ret,mktcap;可选列industry,turnover,pe,pb,ps,bench。缺mktcap直接判定无法履约(WLS 权重与规模因子都依赖它),如实告知用户补齐。 - 确认订阅与付费状态:读下方「订阅状态横幅」一节,先确定本次是已订阅还是按次购买。
- 确认扫描项目:默认跑
all(归因 → 暴露 → 风险 → 偏差检验 → GMV)。用户点名某一项时切到对应--mode。 - 执行履约:
python3 scripts/run_local.py --input <panel.csv> --mode all
[--weights <weights.csv>] [--out <输出目录>] [--svg]
[--subscription "<渠道名>"]weights.csv 列:ticker,weight(缺则默认按最新截面市值权重构造组合)。
- 出报告:按
references/output_templates.md的模板渲染,结论先行;取不到的项标「未获取」,绝不编造。 - 交付:报告首位固定输出订阅状态横幅,末尾固定免责句(见
references/output_templates.md)。
Output(输出规范)
订阅状态横幅(每次输出固定置于首位,不可省略)
每条报告的第一行必须是订阅状态横幅,二选一:
- 已订阅:
已订阅 · 您已通过「<subscription.channel>」订阅此技能,本次调用计入订阅额度 - 未订阅:
未订阅 · 请先购买订阅;单次 5.00 元,或在 SkillHub 订阅后按额度调用
渲染成带边框的横幅块(终端用 ═ 框,HTML 用卡片),已订阅为绿系、未订阅为橙系。判据优先级:--subscription 参数 > 环境变量 PORTFOLIO_XRAY_LAB_SUBSCRIPTION > config.yaml 的 subscription.channel > 空则判为未订阅。
五类结果
| 模式 | 输出 | 关键字段 |
|---|---|---|
attribute | 收益归因 | 纯净因子收益序列、t 值与显著占比、组合收益分解(各因子贡献占比 + 残余 alpha)、回归 R² |
exposure | 风格暴露 | 9 类风格因子暴露百分位(50% 为中性,±10% 为中性带)、共线性 VIF、因子相关强度 RSI |
risk | 风险预测 | 组合年化预测波动、因子协方差贡献 vs 特异风险贡献拆分、逐期预测波动序列 |
bias | 偏差检验 | 偏差统计量 B、95% 置信区间 [1-√(2/T), 1+√(2/T)]、RAW/NW/Eigen/VRA 四档对比、低估/高估判定 |
gmv | 最小方差组合 | long-only GMV 权重、与基准的年化波动/年化收益/夏普/最大回撤对比 |
分级规则
- 偏差统计量 B:落在 95% 置信区间内 → 绿(风险测度可信);
B > 上界→ 红(系统性低估风险);B < 下界→ 黄(高估风险)。 - 暴露百分位:
>90%或<10%→ 红(极端风格押注,收益高度依赖该风格延续);>70%或<30%→ 黄(明显偏离);其余 → 绿(中性)。 - 因子共线性:
VIF > 10→ 红(须正交化后入模);5 < VIF ≤ 10→ 黄;其余 → 绿。 - 归因显著性:
|t| > 2的期数占比≥ 50%→ 绿(因子持续有效);20%~50%→ 黄;< 20%→ 红(该因子收益不稳定,不可作为归因主因)。
缺失信息处理
任何因子列缺失 → 该因子整列跳过,并在报告「数据可用性」小节明确列出「未获取:XX 因子(缺 XX 列)」;不沿用上一期值、不用横截面均值填充、不推测补造。
Notes / Constraints(约束)
- 不构成投资建议:所有输出末尾固定一句「本结果仅为量化统计研究,不构成任何投资建议;历史因子收益不代表未来,模型存在失效可能。」
- 只做统计、不做预测涨跌:本 Skill 输出波动率预测与收益归因,不输出买卖信号、不给目标价、不做择时。
- 取不到即标「未获取」:数据缺失、样本不足(截面股票数少于因子数 + 行业数 + 2)时不强行回归,直接标注并返回非 0 退出码。
- 不访问外部系统:不联网取行情、不调任何第三方 API;全部输入来自用户提供的 CSV 文件。
- 蒙特卡洛次数可调:默认 500 次以控制单次耗时;追求与文献一致的估计精度时可设
--mc-sims 10000,耗时显著上升。 - 频率假设:面板按交易日或月度均可;
horizon_days=21在日频下为「未来 1 个月」,月频下需自行换算为1。 - long-only 约束:GMV 优化限定权重非负且和为 1(A 股做空机制受限),因此结果不等同于无约束解析解
V⁻¹α/αᵀV⁻¹α。
Pay Skill 模式(鉴权型,按次付费,已接入 X402)
本 Skill 为付费技能(Pay Skill),按次收费,已通过腾讯 SkillHub + 微信支付 Agent Pay X402 协议接入。不存在免费运行模式——未完成支付授权前,不得执行本地履约脚本、不得输出任何付费内容;鉴权型只由服务端返回的 paid: true 解锁本机履约。
使用者须已安装并开通
weixinpay_pay插件(微信 AI 支付 / AI 专属卡)。Step 0 检查不通过时不发起付费调用,直接引导开通后重试。
鉴权型:履约在本机执行(需调用本地取数脚本 / 解析本地或远程大文件),远端网关只负责订单鉴权与支付状态确认,支付通过后返回 fulfillment: "agent_local",不返回业务结果;Agent 收到 paid: true 后才在本地跑 scripts/ 下的脚本产出结果。
- 统一网关主接口:
POST https://pay.zerone-iteration.com/api/invoke
(请求体 {"skill": "portfolio-xray-lab", "skill_version": "1.0.1", "query": "...", "amount": 500})
- 兼容入口:
POST https://pay.zerone-iteration.com/api/resource(等价于默认 Skill) - 支付回调:
POST https://pay.zerone-iteration.com/api/pay/notify - 退款回调:
POST https://pay.zerone-iteration.com/api/refund/notify - 实现:透明网关(nginx + Java 17 / Spring Boot + WechatPay-Java,微信支付公钥模式);
skill/skill_version 仅做格式校验后透传进 X402 预下单,不查 SkillHub(不查注册表、不反查是否真实存在/已发布/版本匹配);无本地白名单,新增 Skill 免 Java 注册、免改 JAR、免重启,直接 POST /api/invoke 传正确 slug+版本即可
- 状态:已实测。DEV_MODE 下
/api/invoke首次调用返回 402(WeixinPay-Required头与 body
双通道携带 X402 payment_code,附 X-Out-Trade-No),模拟回调置位后带订单号重试返回{"ok":true,"paid":true,"fulfillment":"agent_local","already_fulfilled":false};真实 A 股月线面板(112 只 × 156 个截面,2013-02~2026-01)上 scripts/run_local.py --mode all全链路跑通,见 REALDATA_VALIDATION.md。
Agent 侧调用规范(含付费前置插件检查)
Step 0 付费前置插件检查(必须,缺失则不得发起付费调用)
- 检测当前环境中
weixinpay_pay插件(微信支付 Agent Pay / AI 专属卡)是否已安装并已开通; - 若不存在或未开通:不调用
/api/invoke,改为提示用户先安装/开通微信 AI 支付,完成后再继续; - 若可用:继续 Step 1。
Step 1 调用统一网关:POST /api/invoke,请求体:
{
"skill": "portfolio-xray-lab",
"skill_version": "1.0.1",
"query": "用户原始需求(用于订单留痕,不作为履约输入)",
"amount": 500
}skill:必填,固定为本 Skill 的 slugportfolio-xray-lab(与 SkillHub 已发布的一致;透明网关下传错 slug 时错误延后到 SkillHub/微信预下单环节才返回);skill_version:必填,当前版本号1.0.1(缺失返回400 MISSING_SKILL_VERSION);amount:必填,单位分,服务端校验为大于 0 的整数(小数/布尔/零/负数一律400 INVALID_AMOUNT,不静默截断);
区间不强制,价格由 SkillHub 产品定价约束。
Step 2 支付触发(X402 预下单 → 402):未支付时服务端依次执行——
- 生成订单号
out_trade_no(402 响应头X-Out-Trade-No与响应体out_trade_no均会返回,Agent 须记下); - 调用微信支付 Native 下单,获取
code_url; - 用 SkillHub 开发者私钥(SHA256withRSA)对 X402 AI 预下单请求签名;
- 调用
POST https://payapp.weixin.qq.com/palmpayminiapp/clawagentpay/preorder换取payment_code; - 返回 HTTP 402,响应头
WeixinPay-Required与 bodyWeixinPay块中
同时携带 X402 预下单返回的 payment_code(必须是 X402 的 payment_code,不能直接把微信 code_url 当作 payment_code 返回),并回显本单 amount 与 out_trade_no。⚠️ body 的 WeixinPay 嵌套结构最易遗漏:缺失时 weixinpay 插件从 body 解析不到payment_code,会报「当前支付订单已过期」,该报错常被误判为「返回了 code_url」。
Step 3 用户支付授权:Agent 将 WeixinPay-Required 头(或 body WeixinPay 块)中的payment_code 作为 paymentCode 交给 weixinpay_pay 插件,向用户申请支付授权。
Step 4 二次调用鉴权:用户完成支付后,Agent 再次调用 POST /api/invoke(请求体同 Step 1,带请求头 X-Out-Trade-No: <402 返回的订单号>);服务端按订单号主动向微信查单验证支付成功(不依赖回调到达)后返回:
{"ok": true, "paid": true, "fulfillment": "agent_local",
"skill": "portfolio-xray-lab", "out_trade_no": "...", "already_fulfilled": false}Step 5 本地履约(本 Skill 的关键差异):Agent 收到 paid: true 且fulfillment: "agent_local" 后,在本机执行 scripts/ 下的脚本产出结果(见 scripts/README.md),再把结果交付给用户。支付未确认前,不执行本地取数脚本、不输出任何付费内容(未支付重试返回 402 {"code":"NOT_PAID"})。
幂等说明:
already_fulfilled: true表示同一订单此前已鉴权通过;此时直接复用上次已产出的结果,不要重复跑取数脚本(避免重复消耗外部接口配额)。
异常处理说明(Agent 与服务端两侧)
| 异常场景 | 处理方式 |
|---|---|
weixinpay_pay 插件不可用 | 不发起付费调用,引导用户安装/开通微信 AI 支付后重试 |
| 用户拒绝支付 / 支付取消 | 如实告知用户支付未完成,不推进后续流程、不输出付费内容;可重新发起(服务端生成新订单) |
| 支付失败(余额不足/风控拦截) | 如实转达失败原因,引导稍后重试;失败订单作废,不据此履约 |
payment_code 过期(最长 15 分钟) | 提示订单已过期,Agent 重新调用获取新订单与新 payment_code,不得复用旧码 |
| 报错「当前支付订单已过期」 | 先查 402 响应体是否有 WeixinPay 嵌套结构,其次才是 code_url 方向 |
| 支付成功但二次调用仍 402(回调延迟) | 等待 3~5 秒后重试,最多 3 次(指数退避),携带 X-Out-Trade-No、订单号不变 |
| 网络异常 / 5xx | 指数退避重试(1s/2s/4s,上限 3 次);持续失败告知服务暂不可用 |
| 本地取数脚本失败(数据源不可用/文件缺失) | 如实告知用户并说明原因,不得因为已付费而编造数据;可引导稍后重试同一订单(already_fulfilled 为 false 时不会重复扣费) |
| 金额校验失败(400 INVALID_AMOUNT) | 修正 amount 为大于 0 的整数分后重试 |
| skill 传错 / 未发布(错误延后到 X402/微信预下单环节) | 核对 skill/skill_version 与 SkillHub 已发布条目一致后重试,不得随便换 slug |
| skill_version 缺失(400 MISSING_SKILL_VERSION) | 补上当前版本号 1.0.1 后重试 |
| 未支付即重试(402 NOT_PAID) | 等待用户完成支付授权后按 Step 4 重试,不得跳过支付环节 |
| 退款 | 服务端经 /api/refund/notify 接收回调;已退款订单的 payment_code 失效,再次调用将重新走支付流程 |
核心原则(SkillHub 官方教程 FAQ):支付失败/未确认时必须如实告知用户,绝不据此推进后续流程。
核心特征对照表(供审核查验)
| # | 核心特征 | 实现位置 |
|---|---|---|
| 1 | X402 AI 预下单换取 payment_code(开发者私钥 SHA256withRSA 签名,调官方 preorder 接口;WeixinPay-Required 返回 payment_code 而非微信 code_url) | pay/server.py skillpay_x402_preorder();链路 Step 2.3~2.5 |
| 2 | 支付触发(未支付返回 402 + WeixinPay-Required 头/体双通道) | pay/server.py _handle_invoke();链路 Step 2.5 |
| 3 | 订单号传递(out_trade_no 全链路关联:下单→预下单→回调置位→X-Out-Trade-No 重试→二次调用) | pay/server.py generate_trade_no() / mark_paid / 402 响应 X-Out-Trade-No 头 |
| 4 | 重试机制(402 回调延迟重试带 X-Out-Trade-No、5xx 指数退避、订单过期重新下单) | 上表「异常处理说明」 |
| 5 | 付费前置插件检查(weixinpay_pay 可用性检测,缺失不发起付费调用) | 链路 Step 0 |
| 6 | 异常处理说明(支付失败/取消/过期/退款/本地脚本失败等全场景) | 上表「异常处理说明」 |
| 7 | 透明网关(skill/skill_version 仅做格式校验后透传,不查 SkillHub、不查注册表) | pay/server.py resolve_skill()(无 SKILL_REGISTRY,免 Java 注册/重启) |
包内 pay/server.py 为 Python 参考实现(含 APIv3 签名、X402 预下单、回调验签解密、主动查单、幂等鉴权、透明网关无需注册表),部署说明见 PAY_SKILL_UPGRADE.md 与 pay_skill.yaml。
注意:本 Skill 涉及两套独立密钥/证书——
- 微信支付 API 证书(
wechat_pay):用于微信 Native 下单与主动查单; - SkillHub 开发者密钥(
skillhub):用于 X402 预下单签名。
私钥均不得写入 Skill 包或仓库,须通过环境变量注入服务端。