请按【四步】完整跑完一个因子从加工、落库、检验到出图文报告的全流程。每步结束用一两句话说明做了什么和耗时;遇到数据缺失/异常的标的,记录后跳过,不要中断整体流程。 【输入参数】(可覆盖) 因子:季度销售增长率大于5%;季度利润率大于10%; 股票池:全 A,剔除 ST、停牌、上市不足 250 个交易日。 区间:2019-01-01 至最新交易日。 检验频率:月度。信号日收盘后生成,T+1 开盘价买入,持有到下一个月末信号日的次日开盘。 落库表名:aaaa;报告路径:./aaaa.html 【技术约束】(动手前先读,能显著提速) 因子加工必须用 DAI SQL 在引擎内一次完成,不要用 pandas 逐日循环。用平台截面算子,先在 dai-functions.md 里 grep 确认签名: 行业中位数填充:COALESCE(x, c_group_quantile_cont(ind, x, 0.5))。注意 c_fillna 填的是均值,不是中位数。 缩尾:c_winsorize(x, 0.01, 0.99) 标准化:c_zscore(x) 中性化:c_neutralize_resid(x, 行业列, LN(总市值)),取残差。不要用 c_neutralize / c_indneutralize,它们内部自带缩尾和标准化,会和前面的步骤重复加工。 分组:c_ntile(因子, 10) 数据表: cn_stock_prefactors:pe_ttm、roe_avg_ttm、total_market_cap、sw2021_level1。财务字段已按公告日对齐。 cn_stock_status:st_status、suspended。 cn_stock_factors_base:trading_days。该表缺北交所老代码(430/83x/87x 开头),必须 LEFT JOIN,缺失时用 list_days*245/365 折算兜底,否则会丢约 12 万行。 股票池过滤(WHERE)放在不含 c_* 的 CTE 里,让横截面算子只在股票池内计算。 用 filters 一次取全区间,不要按年循环;大查询用 engine="aistudio"。 用到 scipy 等库的脚本,用 bq aistudio exec -- python x.py 运行;直接 python 会落到没有 scipy 的本机。 报告里的所有数字都从结果文件(json)读取,不要手填;结论里没验证过的原因解释,必须标注为「猜测」。 【第一步 · 因子加工】 按上面的算子写成一条 SQL:股票池过滤 → 行业中位数填充 → EP、ROE 各自缩尾 + z-score → 等权合成再 z-score → 对「行业哑变量 + 对数市值」取残差 → 再 z-score。输出诊断:各步剔除/填充条数、中性化前后与对数市值的相关系数、每日均值≈0 且标准差=1、无重复 (date, instrument)。 【第二步 · 保存】 write_bdb 写入 value_roe_neutral:overwrite=True,unique_together=[date, instrument],带 docs 和元数据(因子名、加工日期、加工引擎、中性化方式、股票池、区间)。回读核对行数,打印表名 + 行数 + 路径。 【第三步 · 检验】(从落库表读取因子;先尝试用 SQL 的 c_ntile / c_rank / m_lead 算分组和 IC,对不上小样本核对结果时再回退 numpy) RankIC:逐日对下期收益,输出均值、标准差、IR、t 值、IC>0 胜率。t 值必须用 Newey-West 校正(20 日收益窗口重叠,朴素 t 会虚高好几倍),同时给出中性化前后对比。另输出累计 RankIC 序列。 分组:10 组(G1 最小 → G10 最大)的下期平均收益、G10−G1、组序相关;同时输出 10 组各自的累计净值。 多空:多 G10 空 G1,月度调仓,给出扣费/不扣费的净值、年化、夏普、最大回撤、单边换手;成本假设写明(单边 0.15%)。另给分年度收益,并与全市场等权基准对比。 自检:财报更新日是否紧跟公告日(统计滞后分布与异常笔数)、因子额外滞后后的 IC、随机因子对照、与过去收益的关系、退市标的是否在样本内。 【第四步 · HTML 报告】 自包含 HTML,内联 CSS/JS,离线可用(内联 SVG 或 ECharts 均可),红涨绿跌,全中文。板块: 概览卡 + 一句话总评(有效 / 存疑 / 无效)。 加工说明:步骤清单 + 大白话解释「为什么要中性化」,并用本次中性化前后的 IC 对比佐证。 图表(至少 7 张):RankIC 时间序列(0 轴 + 60 日均线)、累计 RankIC、十组下期收益柱状图、十组累计净值(G1 绿 → G10 红渐变,G1/G10/等权基准线尾直接标注,悬停按净值排序)、多空累计净值(扣费/不扣费 + 基准)、IC 直方图、分年度 IC。 指标表,按课程口径标注:|IC|≥0.03「可用」、≥0.05「强」、>0.1「可疑(警惕未来函数)」;单调性用「台阶状=规律 / 乱跳=巧合」;另附分年度收益表。 结论(大白话):IC 是否显著为正、分组是否单调、多空是否稳定,给「要不要继续研究」的建议。 风险与备注:样本区间、过拟合提示、空头不可实现、交易细节未建模、自检结果(通过 / 有未解释尾部要如实写)。 最后返回报告文件路径,并停止 AIStudio。

先读因子文档和图表文档,并查找季度营收增长、利润率字段。

开始第一步:先核对字段口径、单位和覆盖情况。

字段是小数口径,利润率里有 inf,需要当缺失处理。再确认北交所老代码的上市日期从哪里取。

先在一个月的小窗口上试跑加工 SQL,确认各算子对空值的处理。

小窗口试跑通过:与 numpy 回归残差最大差 2.5e-12,每日均值约 0、标准差为 1,中性化后与 ln(市值) 的相关为 0。现在跑全区间并写诊断。

全区间一次取数在 D0(3.7GB 内存)上 OOM。先看 dai.query 有没有降内存的选项,再决定是否分段。

OOM 在引擎内部。先按报错提示试低内存设置,仍然保持一次取全区间。

因子加工 SQL:单季度营收同比增长 + 单季度销售净利率,行业中位数填充、缩尾、z-score、等权合成、对行业+ln(总市值)取残差。策略文件 · 3.7 KB
gm_daily_diag.parquet
gm_factor.parquet
还有 3 个产物,在右上角「产物」里
请运行文件 `step1_build.py`,检查实际输出,并告诉我运行结果。

重新运行第一步,并完整查看输出(上次只看了末尾)。

退出码为 0,警告都是 pandas 的弃用提示。我的过滤命令正则写错了,现在直接看各年分段的行数和 。

是完整的,但分段进度行没出现在我的过滤结果里。我先看日志本身,再下结论。

日志里缺少我预期的 8 行分段进度,警告也只数到 7 组,需要核实 8 个分段是否都跑完。我直接看日志原文,并按年统计 parquet 的行数。

gm_daily_diag.parquet
gm_factor.parquet
step1.json文件 · 1.4 KB

运行成功,退出码 0,耗时约 2 分 10 秒(脚本内计时 126.5 秒),8 个年度分段全部完成,没有跳过任何一段。

两处与你的要求不一致,先说明:

  • 一次取全区间不可行。 在 D0 免费规格(引擎内存约 3.7GB)上,一次取 2019 年至今会 OOM,加 threads=1 等设置也一样。我改成按年分 8 段,没有升级付费规格。所有算子都是逐日截面计算,分段和一次取的结果应当等价,但我没有拿全区间结果做过对照,因为它跑不出来。
  • 因子口径是我替换的。 你给的因子是「季度销售增长率>5%、季度利润率>10%」,流程模板里却是 EP/ROE。我用单季度营收同比(operating_revenue_mrq_yoy)和单季度销售净利率(net_profit_rate_mrq)替换了 EP/ROE,做成连续的等权合成因子,不是 0/1 筛选。同时保留了「增长>5% 且利润率>10%」的达标标记,日均约 20.6% 的股票达标。表名也按你的参数用 aaaa,没用模板里的 value_roe_neutral。如果你要的其实是 EP/ROE 或二值筛选,需要重做。

运行结果:

  • 样本: 共 8,176,921 行,1,894 个交易日(2019-01-02 至 2026-10-27,即数据里的最新交易日),5,645 只股票,日均约 4,317 只,(date, instrument) 无重复。
  • 股票池剔除(股票·日):ST 剔除 304,466,停牌 15,144,上市不足 250 个交易日 535,889,行业或市值缺失 261。
  • 缺失处理: 增长率行业中位数填充 173,952 条,利润率 133,804 条,没有剩余 NaN。缩尾两个指标各触及 165,248 条。
  • 北交所新代码: 基础因子表缺 176,086 行,全是 920 开头的新代码。这些行的上市日期都能从 cn_stock_basic_info 取到,所以没有丢行,上市天数按 245/365 折算。
  • 中性化效果: 合成因子与 ln(市值) 的日均相关系数从 0.214 降到约 5e-14(单日最大 3e-11),每日均值绝对值最大 3.4e-17,标准差在 0.99999999999999 到 1.00000000000000 之间。

小窗口试跑时,我还拿 numpy 做「行业哑变量+ln市值」回归残差对照,最大差异 2.5e-12。

产出文件在项目目录:gm_factor.parquet(约 217MB)、gm_daily_diag.parquet、、。日志里夹着 pandas 弃用警告,不影响结果。

第一步已完成。第二步(落库 aaaa)、第三步(检验)、第四步(HTML 报告)还没做,是否继续?

继续执行第二、三、四步(落库、检验、生成HTML报告)吗?

继续。写库前先看 aaaa 是否已存在,因为 overwrite=True 会替换已有数据。

aaaa 已经存在:951,986 行、2 列,最后更新于 2025-09-17,没有声明主键和说明文档。这不是本次流程建的。覆盖前我先看一下里面是什么。

备份现有的 aaaa 表,再用 overwrite=True 进行新的写库
用时 2 秒
继续完成量化操作的流程

先备份现有 aaaa(含行数和元数据),核对无误后再覆盖写入。

备份已验证:951,986 行,只有 date、instrument 两列,回读与原表一致。现在写入新的 aaaa。

自动审核暂时没能评估这次写库,提示可原样重试。备份已验证,你也明确授权了覆盖,所以我原样再试一次。

行情表里有 241 只已退市标的,另有 29,577 行开盘价无效(多半是停牌日),这两项后面都要处理。现在再试一次写库。

改写到 value_roe_neutral。overwrite=True 会替换同名表,所以先确认这个名字是否已被占用。

value_roe_neutral 也已存在。覆盖前先只读看它的内容,这个名字像是一张 EP/ROE 因子表,而我写的是另一个因子。

growth_margin_neutral 是空闲的,我用它落库(新建,不会覆盖任何已有数据)。

修正了路径写法,现在重跑一次让 里的路径正确,同时找利润表 PIT 表的真实 id。

覆盖前备份已有的 aaaa 表:数据存 parquet,元数据存 json,并回读核对行数。策略文件 · 808 B
第二步:把因子写入 aaaa(overwrite=True,主键 date+instrument),回读核对。策略文件 · 3.2 KB
aaaa_backup_before_overwrite.parquet
还有 2 个产物,在右上角「产物」里
回答「aaaa 无写权限,因子表改写到哪里?第三步会从落库表读取因子,报告里的表名也跟着变。」:改用 value_roe_neutral (Recommended)
行情表中的 241 只已退市标的和 29,577 行无效开盘价,具体如何在因子计算中过滤或标记? 找到利润表 PIT 表的真实 id 后,后续的因子合成或回测流程如何安排?
用时 4 秒
行情表里有 241 只已退市标的,另有 29,577 行开盘价无效(多半是停牌日),这两项后面都要处理。现在再试一次写库。 改写到 value_roe_neutral。overwrite=True 会替换同名表,所以先确认这个名字是否已被占用。 value_roe_neutral 也已存在。覆盖前先只读看它的内容,这个名字像是一张 EP/ROE 因子表,而我写的是另一个因子。 growth_margin_neutral 是空闲的,我用它落库(新建,不会覆盖任何已有数据)。 修正了路径写法,现在重跑一次让 step2.json 里的路径正确,同时找利润表 PIT 表的真实 id。

我先原样重试被拦的那条命令。上次它没有执行,所以 里的路径还是旧的 aaaa。我把任务 2 提前标成了完成,这是我的疏忽。

step2.json文件 · 389 B
利润表 PIT 表的真实 id 已找到了吗?后续还需要关联其他表做验证吗?

找到了:利润表 PIT 表的 id 是 cn_stock_financial_income_general_pit,date 是公告日,report_date 是报告期。它只覆盖一般企业,银行、保险、券商在别的表里,我还没查。

滞后分布(刚查的)

  • 如果把表里所有记录都算上,公告日距报告期的中位数是 386 天,明显不对。
  • 改成每个报告期只取首次公告日后,中位数是 52 天,5% 分位 24 天,95% 分位 179 天。
  • 异常笔数:4 笔公告日早于报告期末,10,342 笔(约 6%)滞后超过 130 天,最大 1,213 天。
  • 170,498 个报告期里有 142,541 个有多条记录,所以 386 天很可能是调整重述记录拉高的。这一条是推断,我没有逐条核对。
  • 这 10,342 笔长滞后的原因我还不知道,在报告里会标成未解释的尾部。

还要关联其他表吗? 要,有两处:

  • 财报更新日是否紧跟公告日: 我会把这张表的首次公告日,和 cn_stock_prefactors 里 operating_revenue_mrq_yoy 实际发生变化的日期做对比。
  • 收益和退市标的: 下期收益用 cn_stock_bar1d 的开盘价,退市标的也在这张表里查。

241 只退市标的和 29,577 行无效开盘价

  • 我之前说无效开盘价「多半是停牌日」,这只是猜测,没验证过,检验时会核对。
  • 以下是我计划的做法,还没实现。
    • 退市标的留在样本里,不做幸存者剔除。它们最后一根行情之后的下期收益为空,只在对应日期被排除。检验时会统计这 241 只有多少出现在因子表里。
    • 无效开盘价先置为空,再取下期收益,用到它的那一天不进入 IC 和分组。多空组合里,这样的股票买不进,我会按买不进处理。

落库的两处变动,需要你确认

  • aaaa 我没有写权限,写入被拒绝,原表没动。覆盖前做的备份(aaaa_backup_before_overwrite.parquet)没用上,可以删。
  • 你选了 value_roe_neutral 后,我发现它是别人昨天写的 EP/ROE 因子表,我读不了也无法备份,所以没碰它。
  • 我改写到了新表 growth_margin_neutral,这次没有再问你。回读 8,176,921 行,主键无重复。报告路径仍是 。你要用别的表名,我可以改。

第二步完成。第三步检验和第四步 HTML 报告还没做,最后还要停止 AIStudio。

请做第三步检验

第三步分两段:先用 SQL 取数并做小样本核对,再算统计量。

先只跑 2024 年试跑,确认 SQL 和核对逻辑没问题再跑全量。

单日取数直接查有 4,958 行,说明数据没问题,问题出在脚本里的 SQL_RAW。我把它原样取出来分阶段计数。

三处窗口函数的日期过滤已改成 QUALIFY。重跑 2024 年试跑,这次要看抽样核对是否真有数据。

fin_lag.parquet
ic_daily.parquet
me_rows.parquet
还有 4 个产物,在右上角「产物」里
用QUALIFY改写后,三处窗口函数的输出行数分别是多少?和改前对比是否符合预期?

fin_lag.parquet
ic_daily.parquet
me_rows.parquet
还有 1 个产物,在右上角「产物」里
请执行第四步
好的
AI 工作时补充

第四步的报告要读第三步的结果文件,而第三步只试跑过 2024 年,统计部分也还没写。我先跑完第三步取数的全区间,再写统计和报告。

先写统计主脚本,把取数、回测、自检汇总成一个结果 json。

第三步的统计出来了。接下来读自检部分(滞后、随机对照、PIT 披露滞后),再决定报告怎么写。

fin_lag.parquet
ic_daily.parquet
me_rows.parquet
还有 6 个产物,在右上角「产物」里
回答「接下来怎么处理第三步和第四步?」:跑全区间取数并继续 (Recommended)
请按流程继续
用时 3 分
请执行第四步

先读第三步的结果文件和图表文档,报告里的数字都从 json 取。

先看 的完整结构(跳过长序列),确定报告可用的字段。

继续看 后半部分(原始因子回测之后的自检字段)。

先看剩余的自检字段和分组数据,再写报告脚本。

先确认第三步脚本里几个口径(中性化前因子、过去收益对照、剔除计数),报告里的说明才不会写错。

先读图表规范,并核对第一步的字段口径和耗时,报告里才不会写错。

先建任务清单并确认几处口径(多空收益怎么算、渲染工具是否可用),再写报告生成脚本。

回看结果文件后,口径都清楚了。接下来写报告:CSS、绘图 JS 和用 Python 从 json 填数字的生成脚本,三部分分开写。

报告分三份文件生成:样式、绘图脚本、从 json 取数的 Python。先写样式。

先写绘图库(折线、柱状、直方图,带悬停提示)。

接着写柱状图库(十组收益、IC 直方图、分年度)。

先查一个数: 118,050 行 20 日后开盘价为空,有多少来自最近 21 个交易日(还没到期)。这样报告里不用写成猜测。

数字就绪。先写生成脚本的数据层:读各步 json,算好格式化值和总评级别。

-I 不把当前目录加入路径,所以导入失败。改成不带 -I,这里没有不可信文件。

总评为「存疑」。下面写图表初始化脚本,数据由 Python 注入。

总评定为「存疑」。下面写报告前半部分:概览、加工说明和图表区。

下面写报告中段:图表区和指标表,颜色按红涨绿跌。

再写后半部分:自检表、结论、风险与备注。文字里的定性判断都加了断言,数据变了就会报错,不会悄悄写错。

最后写组装脚本:把样式、JS 和数据内联进一个 HTML,然后运行。

那行以单引号开头却以双引号收尾。改成单引号。

构建那次与修改是并行跑的,所以先重新构建一次,再检查离线性、JS 语法和数字是否与 json 一致。

没有浏览器可用,我在 node 里用一个最小 DOM 桩把图表脚本真正执行一遍,查运行时错误和 NaN 坐标。

修正了两处表述后重新构建,并核对报告正文里的数字与 json 一致。然后确认 AIStudio 状态。

第四步 · 补充统计:从 ic_daily.parquet 汇总报告要引用、但 step3b.json 里没有的数字,写 step4_extra.json。策略文件 · 1.5 KB
第四步 · 数据层:读第一到三步的结果 json,算出报告要用的全部数字与评级。报告里不手填数字。策略文件 · 2.6 KB
报告前半:标题、总评、概览卡、加工说明、图表区。所有数字取自 s4_ctx 读入的 json。策略文件 · 6.4 KB
还有 5 个产物,在右上角「产物」里

第四步完成,报告在 (144KB,自包含,离线可用,全中文,红涨绿跌)。AIStudio 已是停止状态,不用再停。

总评:存疑。 中性化后日 RankIC 均值 0.0240,Newey-West t 值 3.86,显著为正,但低于 0.03 的「可用」线。朴素 t 值是 14.4,约为 NW t 的 3.7 倍。多空扣费后年化 6.67%,月度 t 值 1.86,没过 2。2021 至 2023 年三年扣费后为负。G10 低于 G9,多头扣费后年化低于等权基准 14.52%(报告里 G10 年化 14.79%,两者基本持平)。

自检:

  • 通过:额外滞后 5 日和 20 日后 IC 仍显著,5 个随机因子 NW t 最大 1.81,SQL 与 scipy 在 3 个抽样日一致,退市标的在样本内。
  • 有未解释尾部:财报变化日异常 2,572 笔,其中 27 笔早于其后 10 天内的公告日,是潜在未来函数。利润表首次披露滞后超过 130 天的有 10,342 笔,原因都没查,报告里标成「猜测」。
  • 因子与过去收益有正关联,没有拆解,也标成「猜测」。

报告内容:

  • 包含 8 张图:RankIC 时间序列、累计 RankIC、十组收益柱状图、十组净值、多空净值、IC 直方图、分年度 IC、分年度收益。
  • 另有 5 张表和结论、风险备注。
  • 数字由脚本从 json 读取,没有手填。
  • 我在 node 里用模拟 DOM 跑了图表脚本,没有错误,坐标也没有 NaN。
  • 没有浏览器可用,版面没有渲染检查过。

三点需要你注意:

  • 因子是我把营收增长和销售净利率做成连续合成因子,不是你原来的「增长>5% 且净利率>10%」二值筛选。该二值筛选没有单独检验。
  • 因子实际落在 growth_margin_neutral。aaaa 没有写权限,写入被拒,原表没动。
  • 报告里的 IC 区间到 2026-09-18,因为需要 20 日后收益,最后 21 个交易日没有纳入。

文件都在 /home/aiuser/work/cowork/2026-10-28-144708/:

  • s4_lib.js
  • s4_bars.js
  • s4_init.js
  • s4_style.css
下一步

内容由 AI 生成,请核实重要信息