股票横截面动量回测案例:跳过最近一个月再做月度排名
月度横截面动量比较股票之间的相对强弱,不是判断某只股票是否高于自己的均线。先冻结美股历史股票池,计算剔除最近一个月的过去收益,在同一决策日比较合格股票,选择前二十名,再于之后允许的开盘交易。形成期价格和成交价格必须分别留存。
本指南提供二十三只合成股票、两次月度选择和费用账。价格用于暴露窗口和替换机制,没有真实历史业绩、参数优化或超额收益承诺。CoTrading策略案例库仍属于规划或待核实,也没有运行行情供应商或券商。交付目标是每月名单与现金可以被复算。
十二减一先写清端点
设月末完整价格为P[m],本例分数为P[m−1]除以P[m−12]再减一。从P[m−1]到P[m]这一最近月收益被跳过,保留区间实际有十一个月度收益。必须写端点,不要只靠“十二减一”名称,因为不同系统可能把月份编号理解为不同位置。
Kenneth French动量构造原始说明使用此前第二至第十二个月的收益。它为跳过月约定提供方法参考,但原因子还包含规模分组和多空组合。本例只是选二十只做多股票,不复制该公开因子,也不能把其历史结果移用到这里。
排序价格要有拆股、分红处理约定,账户中的股数和现金也要与其衔接。供应商复权收盘不自动等于可成交开盘。保存历史成员、代码变迁和端点可得状态。如果只能取得今天的标普五百名单,应明确写成当前成员回溯且存在幸存者偏差,不能称历史标普五百策略。
固定资格与名额
每次决策要求两个形成期端点存在,分母为正。本样例只检查端点;正式总回报计算还需规定中间观察缺失和公司行动有效性。缺数据是一种排除状态,不等于零动量,排除表保留具体原因。数据不足与真实过去收益为零,在经济含义和排序行为上都不同。
对全部合格股票按分数降序,精确同分按稳定证券标识决胜,选前二十。若不足二十,本指南的实际研究契约应让空缺名额保留现金,不能默默提高其余股票权重。本例两个日期都有足够股票,实际检验完整二十名选择,并不演示不足名额的重新分配。
月末完整收盘后排序,之后才允许成交。退出与进入应一起核对资金,不让输入顺序优先满足第一只股票。数量取整、可用现金、订单费和未成交目标写入执行记录。排名或入选标记只是目标资格,不证明已经发生交易,也不能直接拿来计算账户净值。
重建二十三只股票排名
代码S01至S23对应编号一至二十三。每只股票起点十,之后月末价按“十加编号乘月份再除一千”产生,月份从一到十三。决策月份十二使用端点十一与零。因此S23分数为十点二五三除十减一,等于百分之二点五三。这个公式只是透明造数方式,不代表价格模型。
首期名单按顺序从S23到S04,S03、S02、S01落在二十名之外。这是相对排名规则;换一组数据,即使分数为负,也可能进入相对最高二十。如果还想要求绝对动量为正,应另外预设过滤,不能误以为“前二十名”自动包含正收益要求。
把S23端点十二设为缺失。决策十二不变,因为其形成期到端点十一为止。到了决策十三,端点十二成为必需的结束价格,S23就不合格。第二期名单从S22到S03,十九只继续保留,S23退出,S03新进入。两期之间的资格变化有明确数据原因。
验证最近月确实被跳过
独立边界测试把S01端点十二替换为九万九千九百九十九,再重算决策十二。由于该端点不在当前形成区间,成员必须不变。脚本先保存两次正常选择,再做扰动,避免把这个夸张输入误带入原本用来检验S23缺端点的第二次决策。
若实现错误使用P[m]而不是P[m−1],就会读取这个巨值,可能把S01提前提升入选。它属于窗口定义错误,与下一期S23因为必需端点缺失而退出的机制不同。两个案例同时说明:当前不该读取的端点必须无关,但随着时间推进,之前不读取的端点可以合法成为新输入。
两次篮子与费用闭合
执行样例另行给出所有交易下一开盘均为十,后续估值也为十。这是明确输入的合成开盘,不从形成期收盘推断。初始资金一万,二十只各买四十股,每张订单支付虚构费用一。名义金额八千,首期费用二十,剩余现金一千九百八十。
第二期十九只不变,不重复买卖。S23四十股按十卖出,付费用一;S03四十股按十买入,再付费用一。现金剩一千九百七十八,仍有二十只共八百股,给定价十估值八千,最终权益九千九百七十八。两期费用总计二十二。
这条平价执行账故意只产生费用损耗,不能拿来宣称排名预测了百分之二点五三的账户收益。形成期收益描述过去排序信息,之后的成交和估值才决定账户路径。完整历史研究需要区间内所有估值、实际给定开盘、相应成本与拒单,而不能只凭两张月末排名表拼出净收益曲线。
执行样例与历史扩展
标准库文件为docs/growth/research/round-5/examples/R584.py,端点计算如下:
start = prices[symbol][month - 12]
end = prices[symbol][month - 1]
score = end / start - 1
运行python docs/growth/research/round-5/examples/R584.py。断言检查两份精确二十名列表、S23首期分数、跳过月扰动不改变首期成员、替换成员、费用二十二、现金一千九百七十八和权益九千九百七十八。不能只检查入选数量二十,或平均分数相近,因为错误成员也可能通过这类弱检查。
换成真实数据后,每月保存端点日期、当时股票池、排除原因、排名、目标数量、实际订单、成交和估值。区分财务或价格修订造成的分数变化,与月份自然推进造成的窗口变化。真实收益之外报告换手和现金暴露,比较变体前冻结规则,不依据结果临时换名额或跳过月。
数据、历史成员、公司行动或时间约定变更后,应另存版本并重跑名单与现金桥。验收完成条件是独立读者能重建每个月的二十名及替换成本,不是累计曲线看起来向上。如果只有当前成员或缺少退市数据,结果可以作为有限样本探索,但限制必须跟着图表和输出存在,不能在方法段写过一句后就把它称为完整历史策略。
下载完整示例,保存为R584.py,使用Python 3.12及以上运行python R584.py。仅使用标准库与合成数据。
排名换手与资格退出分别解释
给每个成员替换写原因。本例S23离开,因为必要端点不可用;S03进入,因为空出名额使排序边界延伸到它。这与完整面板里S23相对动量落后于S03不同。两种过程可能产生相同的一卖一买,单看换手数无法解释发生了什么,也不能把数据缺失导致的退出写成策略主动看弱。
月度复核表至少保存两个端点日期、合格股票数、第二十和第二十一名,以及影响边界的全部排除。边界候选很重要,小幅价格修订可能只换掉一只,组合平均动量却几乎不变。不能利用这份敏感度去看后续收益再挑规则;应报告修订,按冻结方法重建,再比较订单。历史成员或退市回报缺失时,限制应跟着结果出现。
若采用市值筛选或流动性过滤,还要保存其当时可得输入,不能在固定排名之外悄悄加入今天的成交量。调仓日也应以市场交易日为准:月末不营业时,先确定最后完整交易日,再指定后续执行开盘。新加入的过滤和日历处理各有自己的验收路径,不能因为十二减一算式通过,就假设历史投资资格和成交安排也已解决。
对照另一份实现时,先比较形成端点和资格,再比较排名,最后比较成交数量。相同的期末权益可能只是不同错误互相抵消,不足以证明规则一致。至少抽查入选边界与一次成员退出,确保验收落在实际机制,而不是一个碰巧相等的汇总数字。
常见问题
这是French公开动量因子吗?
不是。这里只借鉴跳过月约定,组合构造是不同的二十股做多规则。
最近月是不是永远不重要?
它不进入本月分数,但到下一月可能成为必需形成端点。