因子分域检验
下载地址
https://www.zhengdianxue.com/wp-content/uploads/skills/zerone-iteration--factor-domain-split.zip将提示词发送给你的 AI 安装该 skills
请根据 https://skillhub.cn/install/skillhub.md,安装 @zerone-iteration/factor-domain-split。把同一个因子放进不同股票域里分别检验:扫描市值/流动性/波动等候选分域变量,用域间 IC 差异的配对 t 检验与域内 IC/ICIR 提升判定「这个分域方式到底有没有用」,并给出连续型加权(w=x^n/mean(x^n))、时序分域(阈值极值修正与高低域期选择)与多因子分域合成的可复现零依赖实现。只做因子研究与统计归因,不构成投资建议,不输出股票名单。
阅读全文 (展开查看完整说明)
概述
因子分域构建器
Pay Skill 范式:鉴权型(agent_local)
鉴权型:履约在本机执行(需读取本地因子面板 / 调用取数脚本),远端网关只做支付鉴权,支付通过后返回
fulfillment: "agent_local",Agent 再跑scripts/下的脚本产出结果。
Overview
这是一个「因子分域」研究工具,不是选股工具。
传统多因子模型把全市场股票按因子值一视同仁地排序打分,隐含假设是「同一因子对所有股票的作用方式和强度相同」。这个假设通常不成立:低估值大盘蓝筹与高估值小盘成长的可比性很差,同一个因子在两类股票里的方向与强度可以完全不同。因子分域就是把股票先切成若干子样本(域),在域内差异化地计算、评估与合成因子。
本工具回答四个问题:
- 该不该分域——给一批候选分域变量(市值/流动性/波动/动量/估值……),逐个做「域间 Rank IC 差异的配对 t 检验」+
「域内 IC/ICIR 相对全域的提升」检验,输出 ADOPT / INCONCLUSIVE / REJECT 三档判定,并自带 Bonferroni 多重检验校正;
- 怎么分域——截面离散型(按分位数切 N 域)、截面连续型(
x = 1/D,w = x^n / mean(x^n)加权,n 网格扫描看敏感性)、
时序分域(阈值极值修正 / 按分域变量挑高 K 低 K 期)三条路径各自给出可复现实现与对比口径;
- 分域后怎么用——域内各自标准化再合成,与全域等权合成做 ΔIC 配对检验,判断分域合成究竟带来增量还是只引入噪声;
- 会不会是过拟合——内置样本量闸门、域数敏感性扫描、前后半段稳定性(域漂移)检查三重体检,
把「只在某个特定参数下才显著」的伪结构挡在门外。
方法来自因子分域建模框架(研报《因子分域初探:确定分域方式》),本 Skill 把其中可迁移的判定链条与三条分域范式沉淀为纯 Python 标准库的零依赖实现,数值口径与原文公开结果可交叉校验。
三条分域范式对照
| 范式 | 适用问题 | 本 Skill 子命令 | 关键参数 |
|---|---|---|---|
| 截面离散型 | 哪个分域变量能真正区分因子效力? | scan / eval | 域数 n_domains、单域最少股票数 |
| 截面连续型 | 不做硬切分,改为按分域变量连续加权 | weight | 幂次 n、变换 inv/invlog/rank、方向 |
| 时序分域 | 因子计算过程里的时间分量应区别对待 | tssplit | 窗口、阈值、高低各取 K 期 |
| 分域合成 | 多因子在域内各自合成再汇总 | compose | 因子列、分域列、域数 |
边界(明确不做)
- ❌ 不输出股票名单、不做因子打分排序、不给买卖建议——只做统计归因;
- ❌ 不预测涨跌、不做择时;
- ❌ 不自动联网抓取行情。面板由用户提供(CSV),或用包内
scripts/fetch_tencent_monthly.py
拉取公开月线自行构造(纯标准库,可离线核对源码);
- ❌ 不替代合规披露与实盘决策。
When to Use(触发条件)
- 用户说「我这个因子 IC 只有 0.02,是不是该分域看看」「不同股票池里因子表现差很多,怎么量化」时;
- 用户说「市值/流动性/波动/行业,哪个拿来分域最有效」「帮我扫一遍候选分域变量」时;
- 用户说「按 ROE 同比把股票分两组后,反转因子在小组里明显更强,这个结论显著吗」时;
- 用户说「我想按市值给估值因子加权,但不想硬切成大小市值两组,有没有连续的做法」时;
- 用户说「因子在极端行情(暴涨暴跌日)里的表现和平时相反,怎么处理」时;
- 用户说「多因子等权合成太粗,能不能分域合成、和等权比有没有增益」时;
- 用户说「我扫了十几个分域变量挑出一个显著的,这能信吗」时(多重检验与过拟合体检);
- 用户已经有
date,ticker,factor,ret面板,想量化「分域到底带来多少增量」。
排除项:「按这个因子给我一份股票池」「哪只票值得买」→ 属选股与投资建议,不做。
Workflow(工作流)
付费前置:本 Skill 为鉴权型。必须先按文末「Pay Skill 模式」完成 X402 支付鉴权,拿到
paid: true后才执行下面的 Step 1 及之后。支付未确认前不读取任何数据文件、不输出任何结果。
Step 0 澄清输入(不要凭空假设)
| 项 | 说明 |
|---|---|
| ① 要做什么 | 扫描分域变量(scan)/ 深评单个分域变量(eval)/ 连续型加权(weight)/ 时序分域(tssplit)/ 分域合成(compose) |
| ② 面板格式 | scan、eval、weight 吃 date,ticker,factor,ret[,domain1,domain2,...];tssplit 吃 date,ticker,ret[,domain] 长表;compose 吃 date,ticker,ret,f1,f2,... |
| ③ 收益口径 | ret 必须是该截面之后一期的收益,小数形式(0.0131 = 1.31%)。给的是百分数(1.31)必须先除以 100 |
| ④ 频率 | 月频 --periods-per-year 12(默认)、周频 52、日频 252。与面板频率必须一致,否则年化失真 |
| ⑤ 分域变量 | 必须与 factor、ret 同期已知(无前视)。行业/风格等类别变量需先转成数值列再传入 |
Step 0.5 环境自检(建议首次使用时跑一次)
python3 scripts/run_local.py demo --out demo.md用合成面板跑通「分域 → 判定 → 输出」全链路:对照组应全部 REJECT/INCONCLUSIVE,实验组的 liquidity 应判为 ADOPT 且 |t| 显著大于其他变量。结论无投资含义,仅排查环境。
Step 1(扫描候选分域变量)
python3 scripts/run_local.py scan
--input panel.csv --domains mktcap,liquidity,vol12,mom12
--n-domains 2 --min-stocks 30 --min-periods 24 --out scan.md输出:全域基准 + 每个候选变量的判定(ADOPT / INCONCLUSIVE / REJECT)、ΔIC、t 值、前后半段稳定性、已按候选个数收紧的 Bonferroni 阈值,按 |ΔIC t| 降序排列。
Step 2(深评入选的分域变量)
python3 scripts/run_local.py eval --input panel.csv --domain liquidity
--n-domains 5 --out eval.md输出:逐域 IC/ICIR/t、域内分位数组合多空年化与夏普、域间 ΔIC 配对检验、两域 IC 同期相关系数、前后半段稳定性、域数敏感性(2/3/5/10)。
Step 3(连续型分域加权,可选)
python3 scripts/run_local.py weight --input panel.csv --domain mktcap
--transform inv --n-grid 0,0.5,1,2,3,4,5,6,8 --out weight.md输出:每个 n 下的 Rank IC / ICIR / 多空年化 / 多空夏普 / 月度胜率,n=0 即原始因子基准。
Step 4(时序分域,可选)
python3 scripts/run_local.py tssplit --input ret_panel.csv --mode flip
--window 12 --threshold 0.15 --thr-grid 0.05,0.10,0.15,0.20,0.30 --out ts.md
python3 scripts/run_local.py tssplit --input ret_panel.csv --mode domain
--domain volume --window 12 --k-pick 6 --out ts2.md输出:原始因子 vs 改进因子的 IC/夏普/胜率对比 + ΔIC 配对检验 + 阈值敏感性网格。
Step 5(多因子分域合成,可选)
python3 scripts/run_local.py compose --input multi.csv
--factors f_rev,f_vol,f_sue --domain liquidity --n-domains 2 --out compose.md输出:单因子基准、全域等权合成基准、分域合成结果,以及 ΔIC(分域 − 等权)配对检验。
Output(输出规范)
- 默认输出中文 Markdown 报告(
--out指定路径,不指定则打印到 stdout); - 数字口径:收益率与占比用百分比(2 位小数),IC / ΔIC 保留 4 位小数,ICIR / t / 夏普保留 2 位;
- 显著性标记:
|t| ≥ 3记***,|t| ≥ 阈值记**,|t| ≥ 1记*,否则不标记;
阈值为 max(2.0, Bonferroni 校正后的 z),多候选扫描时自动收紧;
- 缺失处理:取不到的数据一律写「未获取」并说明原因(列名不匹配 / 期数不足 / 单域股票数不足),
绝不因为用户已付费而推测补造;
- 结论纪律:
- 域间差异不显著时必须明确说「该分域变量未提供增量信息」,不包装成「有一定效果」;
- 出现「|IC| 略降但夏普、胜率明显上升」时,如实描述为更稳健而非更强,不夸大成分域成功;
- 只描述统计事实,不评价「该用/不该用」,不输出任何股票名单或标的排序。
Notes / Constraints(约束)
- 免责声明:本 Skill 产出的是因子研究的统计结果,不构成投资建议,不预测未来收益。
每份报告末尾固定带风险提示(因子拥挤、过拟合、样本外失效)。
- 样本量底线:默认单域单期 ≥ 30 只、共同期数 ≥ 24 期,否则判为「证据不足」而非硬算;
分域会把样本切小,样本量不足时分域检验的功效会急剧下降,这是分域研究最主要的失效来源。
- 前视偏差:分域变量必须与因子、收益同期已知。用全样本统计量(如全样本中位数)切分域会引入前视,
本工具的分域阈值按每期截面分位数滚动计算,已规避该问题。
- 域漂移:分域变量的分布会随时间漂移,固定绝对阈值会让「域」的构成逐渐变质。
eval 子命令输出前后半段 ΔIC 检验,两段符号相反或一显著一不显著即提示域漂移。
- 多重检验:扫描 K 个候选分域变量时,即使全部无效,也有约
1-(1-0.05)^K的概率至少挑中一个「显著」的。
工具已内置 Bonferroni 校正与域数敏感性检查,但样本外复核仍不可替代。
- 因子方向:Rank IC 的正负决定多头端在因子高值还是低值侧;评价引擎自动判定方向,无需用户指定。
估值类因子(PE/PB)传入前应先取倒数,使「低估值」落在因子高值端,否则分域结论失真。
- 规模不变量:连续型加权默认
inv变换(x = 1/D)是尺度无关的((cD)^-n归一化后常数抵消);
invlog(1/ln(D))是研报原式,要求分域变量 > 1(如以元为单位的市值);rank 变换抗极值,适合分布厚尾的分域变量。
- 被误当成选股工具时:用户问「那我买什么」时,如实说明本 Skill 是因子研究工具,
不具备选股能力,不要拿分域结果反向拼凑标的推荐。
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": "factor-domain-split", "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 402 结构冒烟已通过;本机履约链路已跑通(demo + 真实 A 股月线面板);
⚠️ 生产真实扣费链路尚未实测,不可声称已通。**
- 已验证(1)本机履约链路:
python3 scripts/run_local.py demo跑通「合成面板 → 分域扫描 →
三档判定」全链路;scan / eval / weight / tssplit / compose 五个子命令在真实A 股月线面板上均跑通并产出结论(详见交付的实测报告)。
- 已验证(2)DEV_MODE 402 结构冒烟(
pay/server.py内存起服,5 步全绿):
① 首调 /api/invoke 未支付 → HTTP 402 + 响应头 WeixinPay-Required + X-Out-Trade-No +body WeixinPay.Required 嵌套结构(code=PAYMENT_REQUIRED);② /api/pay/notify 模拟回调置位支付成功;③ 带 X-Out-Trade-No 重试 → HTTP 200 + fulfillment=agent_local + paid=true 鉴权回执;④ 同 payment_code 再调 → 幂等 already_fulfilled=true;⑤ amount 缺失 → HTTP 400 INVALID_AMOUNT。
- 未验证:X402 真实扣费链路(真实微信商户 + SkillHub 开发者私钥预下单)尚未做生产实测。
- 发布前必做:按
PAY_SKILL_UPGRADE.md第五节,切真实密钥跑 1 元真扣,通过后回填本条为「已实测」。
Agent 侧调用规范(含付费前置插件检查)
Step 0 付费前置插件检查(必须,缺失则不得发起付费调用)
- 检测当前环境中
weixinpay_pay插件(微信支付 Agent Pay / AI 专属卡)是否已安装并已开通; - 若不存在或未开通:不调用
/api/invoke,改为提示用户先安装/开通微信 AI 支付,完成后再继续; - 若可用:继续 Step 1。
Step 1 调用统一网关:POST /api/invoke,请求体:
{
"skill": "factor-domain-split",
"skill_version": "1.0.1",
"query": "用户原始需求(用于订单留痕,不作为履约输入)",
"amount": 500
}skill:必填,固定为本 Skill 的 slugfactor-domain-split(与 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": "factor-domain-split", "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 次);持续失败告知服务暂不可用 |
| 本地脚本失败(面板文件缺失 / 列名不匹配 / 期数不足) | 脚本打印 [未获取] <原因> 并返回 1;如实转达,不得因为已付费而编造数据。可引导修正列名或补充期数后重试同一订单(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 包或仓库,须通过环境变量注入服务端。