保存选股条件后重跑:区分新入选、退出与数据修订
保存选股条件,应该保存可执行规则,以及足以解释下一次结果的输入上下文。只保存几个股票名字,下一次新增公司可能被误认为出现新机会,实际原因却是股票池扩张或盈利分母更正。正确流程是用同一规则分别执行两个保留快照,计算成员差集,再把每次迁移连接到具体输入变化。
本文使用虚构股价和每股收益,固定要求盈利为正且市盈率低于20。它只解释筛选变化,不评价历史投资收益。示例独立于CoTrading运行,对应产品模块仍为规划或待核实。能重跑当前股票池,也不意味着已经获得历史成员数据。
把规则与观测拆成两个对象
本例规则为eps > 0 AND price / eps < 20。两次执行中规则编号、运算符和盈利定义保持一致。修改阈值或分母就新建配置版本;如果两次规则本身不同,应称为规则方案比较,不能把全部成员差异归因于市场或财务数据更新。
每个快照保留完整请求股票池,包括没有通过的记录,附价格日期、财务期间、可获得时间、来源版本及已知修订事件。SEC EDGAR接口说明展示披露和结构化财务数据会更新,说明后来重新取数未必能恢复旧输入。该接口不是历史盈利预测档案,应自行保留研究当时真正使用的数据。
EPS从1.00变为1.50,只说明字段变了,不能单靠数字判定它是新财报、供应商更正还是提取方式变化。字段归因与事件归因要分层记录。更正类别应来自来源通知或明确的合成元数据;如果只有数值差异,就把上游事件写成待查,不能为了叙述完整而补一个原因。
准备两个可逐行复查的快照
旧股票池为AAA、BBB、CCC、DDD、FFF,新股票池为AAA、BBB、DDD、EEE、FFF。股价和每股盈利采用同一虚构币种及分母约定。样例明确给BBB的新EPS加上更正标签,并非根据上涨幅度猜出来。
| 股票 | 旧股价/EPS | 新股价/EPS | 已知上下文 |
|---|---|---|---|
| AAA | 18/1 | 22/1 | 价格更新 |
| BBB | 24/1 | 24/1.5 | 已记录EPS更正 |
| CCC | 15/1 | 不在股票池 | 成员删除 |
| DDD | 21/1 | 19/1 | 价格更新 |
| EEE | 不在股票池 | 17/1 | 成员新增 |
| FFF | 10/1 | 10/1 | 未改变 |
比较前,把旧快照运行两次,并断言成员集合完全相同。这证明固定输入下计算可重复,不证明输入符合真实历史时点;后者仍需保留可获得日期和历史股票池证据。把两个问题分开,才能避免“脚本每次相同”被误写成“没有前视偏差”。
实际生成新增、退出和保留三表
分别执行旧、新快照。旧入选集合为AAA、CCC、FFF,新集合为BBB、DDD、EEE、FFF。新减旧得到新增,旧减新得到退出,交集得到保留。显示时按固定标识排序,避免供应商返回行序改变就看起来像新的筛选结果。
def run(snapshot):
return {symbol for symbol, row in snapshot.items()
if row["eps"] > 0 and row["price"] / row["eps"] < 20}
before, after = run(old), run(new)
added = sorted(after - before)
removed = sorted(before - after)
retained = sorted(before & after)
完整标准库文件为docs/growth/research/round-5/examples/R523.py,用Python直接运行。实际输出新增BBB、DDD、EEE,退出AAA、CCC,保留FFF。断言核对两个成员集和逐股字段原因。Python排序指南支持这里的固定显示次序;按代码排列只是展示规则,不代表研究优先级或投资价值排名。
下载完整示例。使用Python 3.12+,仅需标准库,全部为合成数据;下载后运行python R523.py。
没有入选的记录也应保留。一只股票可能两次都不通过,但输入发生重大修订;一只保留股票也可能在复杂规则中改变了通过路径。本题交付成员迁移,完整字段变更表可以作为附件,两者不是同一份结果。保留表不应被解读成“所有输入完全没变”。
为每个迁移核对触发字段
AAA在EPS固定为1时,价格18变为22,市盈率从18跨到22,因此退出。DDD价格21降到19,EPS未变,因此入选。BBB分母从1更正为1.5,市盈率由24变为16;由于合成输入同时保存更正事件,解释才能写为EPS更正,而不只是EPS发生变化。
CCC退出候选集合的原因,是新股票池里没有它,并非拿到新观测后数值不合格。EEE则是新加入股票池后首次执行即通过。两者都应标记为股票池变化,不能写成基本面改善或恶化。名单数量更大,也可能仅仅来自覆盖扩大,不能直接当成市场机会增多。
如果价格和EPS同时变化,就列出两项,并分别把其中一项恢复为旧值做反事实计算。两项共同作用时应保留联合原因,不能任挑一个便于叙述的故事。规则跨界可以确定,上游事件仍可能未知;把这种不确定性放在对应行旁边,比写进统一免责声明更有用。
用不同机制反例检查错误分支
股票池删除是数值解释无法覆盖的反例。一个粗糙比较器可能给CCC缺失的新行填零EPS,再称它因盈利条件失败。正确分支先检查成员是否存在,再比较字段。CCC旧快照的结果仍对旧股票池有效,新运行只是没有评估它,不能覆盖旧记录。
另一个容易混淆的情况是来源版本变了。新版本不必然对应新的经济披露,也可能是数据商重发修正提取结果。版本编号和事件类别应分开保存,并给事件附来源通知。只有版本号时写“EPS变化,事件待查”,再建立补查任务,不把技术修订描述成盈利预期上调。
重复重跑还要避免覆盖状态。以规则版本、快照编号和运行编号标识结果,同一输入重跑可以核对相同输出,但不应制造一条无法追溯的“最新名单”。若两次结果不一致,先检查输入散列、代码版本和行序依赖,再讨论经济原因。否则随机执行差异也可能被包装成市场变化。
交付可以更新的差异包
差异包包含同规则确认、两个输入引用、新增表、退出表、保留表,以及每次迁移的原因和证据。数量应对账:旧三只,退出两只,新增三只,新集合四只。每个差异标识至少存在于一个对应快照,保留标识则必须两次都通过。无法解释的迁移进入单列队列,不能强行分配给最常见原因。
来源更正后,重算受影响规则并保留旧、新版本;用户改要求时新建规则版本;股票池改变时明确记录覆盖变化。若输入在取数过程中还在更新,先冻结完整快照再开始比较,避免半批新数据与半批旧数据混成一个没有共同截止日的集合。
这些步骤让保存筛选变成可重复执行、可解释更新的研究操作。它交付的是成员迁移与输入证据,而不是把每一次名单变化都当成新的市场信号。后续研究可以再决定是否分析新增公司,筛选系统本身应先把“为什么变化”说清楚。
将比较验收与结果解读分开
分享差异表前,确认两个快照满足同一字段约定,每次迁移都有证据支持的原因,或明确的待解释标签。把旧值、新值直接放在原因旁边,另一位审阅者就能重算BBB从24倍到16倍的变化,无需从长篇叙述里寻找输入。原因文字与触发字段不一致时,应先修表再写结论。
复杂规则还应保存两日逐条件结果。股票可能通过不同的替代分支而保持入选,因此成员不变并不代表入选理由没有变化。成员比较和条件比较分别交付,才能同时回答“有哪些股票变了”和“没变的股票是否换了通过路径”。这也方便后续定向检查哪些来源仍需要更新。
后来出现更正时,不要原地覆盖已分享报告。建立修订运行,标出受影响迁移,必要时把早先比较标为已被替代。旧快照即使含有后来被更正的来源数值,仍是早先计算真正使用了什么的证据,应该能继续追溯,而不是被新结果抹去。
对外展示差异时,先写计算观察,再写需要进一步研究的问题。例如AAA是价格变化触发退出,这不自动解释价格为何上涨;BBB是来源明确的EPS更正触发入选,也不意味着公司当天真的新增盈利。把技术归因与经济解释分开,能让后续研究沿证据继续,而不是在筛选报告中提前完成未经核实的故事。
变化表应保留生成时间与两个快照的各自截止日。若读者需要最新状态,应重新执行,而不是把旧比较表的标题改成今天。一个诚实的过往快照比无法恢复的数据混合更有用,尤其当研究需要回看某次判断究竟基于什么时。
常见问题
成员没变是否说明输入也没变?
不是。输入可以变化而没有越过阈值,应另外保留字段差异。
新快照缺一行就算淘汰吗?
不能。先区分退出股票池、取数失败与来源不可用,再判断是否存在可执行的新观测。