M19 事件驱动与基本面量化¶
层级:L2 因子投资 | 优先级:★★★ | 预计学时:10 小时 对应原书:因子第 5 章的异象;Bernard 与 Thomas(1989) 前置知识:M08 的发表后衰减;M18 的收盘时点。 读书笔记:事件与公告时点 状态:reviewed 数据截至:2026-10|最近复核:2026-10-22
学习目标¶
- 把公告日跳空和公告之后的漂移分成两段,并只把下一段里你买得到的价格算进收益。
- 用手算季节性随机游走的 SUE,分母只用已经公布的意外。
- 用公告前的交易日估计异常收益,不把公告日放进 β。
- 把指数纳入从公告到生效、以及生效之后的价格,和 Greenwood 与 Sammon 的样本分开。
- 内部人成交日和 Form 4 申报日不是同一个可交易时点。
本章知识拓扑图¶
flowchart TB
ANN["公告时点"] --> GAP["跳空:往往在你成交之前"]
ANN --> HOLD["公告之后的持有期"]
EPS["已公布的 EPS"] --> SUE["SUE"]
SUE --> HOLD
PRE["公告前的收益"] --> BETA["β"]
BETA --> AR["异常收益"]
FILE["申报日"] --> TRADE["下一可交易时段"]
学术上的多空事件组合有一条空头腿。只做多、杠杆上限 1.0 的账户只放得下正意外的那一边。
🟢 入门层:跳空和漂移不是同一笔钱¶
1. 公告在收盘之后¶
Ball 与 Brown(1968,Journal of Accounting Research 6(2))用会计盈余和之后的价格说明,盈余公布时价格没有把信息一次走完。Bernard 与 Thomas(1989,Journal of Accounting Research 27:1–36)把公告之后仍沿同一方向走的那一段叫做盈余公告后漂移。这一段不是公告那一下的跳空。
教学价格:公告在收盘之后,公告前收盘 100,下一交易日开盘 103,生效日收盘 104,再往后的收盘 102。
| 算法 | 收益 |
|---|---|
| 开盘相对公告前收盘 | 0.030000 |
| 假装用 100 买到、持有到 104 | 0.040000 |
| 用 103 的开盘买到、持有到 104 | 0.009709 |
| 用 103 买到、持有到 102 | −0.009709 |
3 个百分点的跳空发生在开盘之前。能成交的人拿到的是 0.97 个百分点,不是 4 个百分点。再持有到 102,从开盘算是 −0.97 个百分点。把「收盘到收盘 +4%」写成策略收益,等于假设自己在公告之前已经买到。
2. 指数纳入变薄,不是一条永久的公告日规律¶
Harris 与 Gurel(1986,Journal of Finance)记录标普 500 名单变动时的价格和成交量压力。Greenwood 与 Sammon 在《Journal of Finance》2024 年 12 月在线发表的结果里,纳入的平均异常收益从 1990 年代的 7.4% 降到近十年不到 1%;2010–2020 年剔除的平均异常收益约 0.1%。M02 已经写过:指数基金变大之后,可预测的冲击没有同步变大。
公告日到生效日是一段,生效之后的价格是另一段。上面的 104 和 102 只说明这两段的符号可以相反。它们不是 Greenwood 与 Sammon 的 7.4% 或 0.1%。
季报常常在收盘之后才出来。从昨晚收盘 100 到今早开盘 103 的 3 个点,你已经买不到。能成交的是开盘之后的那一段。 把「收盘到收盘 +4%」写成策略收益,等于假设公告之前已经持有。负的意外若靠做空去赚,只做多账户没有那条腿,至多是不持有。 去年同一季才是淡旺季对得上的预期。除以这只股票自己的历史惊喜波动,弹性大的公司和弹性小的公司才在同一把尺子上。 内部人自己的成交日,通常早于你能看到的申报日。用成交日去改当天的动量,用的是当时还没公开的信息。 指数纳入的跳空会变薄。近十年的幅度不能拿一篇旧论文的合计直接下单。进阶层把 SUE、β 的窗口和事件研究的五步分开。
公告前收盘 100,次日开盘 103,生效日收盘 104,再往后 102。开盘相对 100 是 3 个百分点,这一段在你看到公告时已经走完。 用 103 买到、持有到 104,是 0.97 个百分点。持有到 102,从开盘算是 −0.97 个百分点。把收盘到收盘的 4 个百分点写成策略收益,等于假设公告之前已经买到。
纳入标普 500 的平均异常收益,从 1990 年代的 7.4% 降到近十年不到 1%。2010–2020 年剔除大约 0.1%。104 和 102 只说明两段的符号可以相反,不是这组样本均值。 只做多账户上,负的意外至多是不持有。权重不因为空不出去就摊给正意外的名字。
去年同一季的预期才和淡旺季对齐。SUE 再除以这只股票自己的历史惊喜波动。β 的估计窗停在公告之前,异常收益才是事件窗减去事先拟合的正常收益。
同一笔 0.30 的意外,分母只用已经公布的惊喜时,SUE 是 2.705926。把当期 0.30 也放进标准差,SUE 降到 2.095291。正在公布的数字不能用来缩小自己的信号。 公告前最后一次一致预期若是 1.45、实际盈余 1.50,意外是 0.05。用四个季度以前没有更新的 1.20,会制造 0.30 的假意外。 内部人的成交日通常早于申报日。用成交日去改当天的持仓,用的是当时还没公开的信息。
🟡 进阶层:SUE 的分母和 β 的窗口¶
3. 季节性随机游走¶
零售的第四季通常高于第三季。用上一季当预期,会把淡旺季当成意外。 去年同一季 Eₜ₋₄ 在公告之前已经公布,季节对得上。 意外还要除以这只股票自己的历史惊喜波动 σ。弹性大的公司,同样 0.30 的意外没有弹性小的公司那么意外。 SUE 把不同弹性拉到同一把尺子上。分母若混进当期,波动会被这次惊喜自己抬高。
一季的未预期盈余用四季之前的每股盈余做预期:
uₜ = Eₜ − Eₜ₋₄
| 符号 | 含义 |
|---|---|
| Eₜ | 刚刚公布的这一季每股盈余 |
| Eₜ₋₄ | 四个季度之前已经公布的每股盈余 |
| uₜ | 季节性意外 |
| σ | 更早若干期 u 的样本标准差,分母是 n − 1 |
| SUE | uₜ/σ |
已经公布的四期意外是 0.10、−0.05、0.20、0.00。当期意外是 0.30。样本标准差是 0.110868。
SUE = 0.30/0.110868 = 2.705926
若把 0.30 也放进标准差,σ 变成 0.143178,SUE 变成 2.095291。当期意外还没公布时,这个更大的 σ 并不存在。用它做分母,是在用正在公布的数字去缩小自己的信号。
分析师一致预期要带时间戳。实际盈余 1.50。公告前最后一次预期若是 1.45,意外是 0.05。若误用四个季度以前、没有更新的 1.20,意外变成 0.30。若数据库里的「当季一致预期」在公告之后被改成 1.50,意外变成 0,信号被事后修订吃掉。能用的是公告时点之前已经发布的那一版。
4. 异常收益的 β 停在公告之前¶
β 的窗口必须在公告之前结束。用公告之后的收益去拟合「正常」,等于用结果解释结果。 异常收益是事件窗收益减去这个事先拟合的正常收益。跳空和漂移仍要分开记。 教学例子里,含进公告日之后,β 会从 1.035294 变成 1.192344。窗口停在公告之前,才是 0.032824 那一格。 负意外的空头仍不在只做多账户里。
公告前五日,市场收益是 0.01、−0.02、0.015、0.005、−0.01,股票收益是 0.012、−0.018、0.020、0.004、−0.008。带截距的回归给出截距 0.002000、β 1.035294。公告日股票收益 0.04、市场收益 0.005 时,异常收益是
0.04 − (0.002000 + 1.035294 × 0.005) = 0.032824
把公告日放进同一条回归,β 变成 1.192344,异常收益变成 0.026699。公告日自己把 β 拉高,再把自己的异常收益压低。估计窗口要在公告之前结束。
空头腿仍放不进只做多的账户。负 SUE 的一边若留成现金,组合就不是论文里的多空。费用按 M13 的每一元成交计算。公告日换仓通常比月度因子更密,不能把未扣费的异常收益当成净值。
5. 事件研究是五步,不是一根收益柱¶
MacKinlay(1997,Journal of Economic Literature)把事件研究收成一条可以复述的流程:先定义事件,再用事件之前的窗口估计正常收益,然后在事件窗里减掉正常收益,加总成累计异常收益,最后看这个累计值相对估计误差大不大。本章只用上面那五天和公告日做手算,不重做他文中的任何样本。
五步落到这组教学价格上,是这样:
- 事件与事件窗。 盈余在收盘之后公布。学术上常把下一交易日收盘相对公告前收盘的收益当作事件日收益,这里就是 100 到 104 的 0.040000。能下单的人在开盘已经面对 103,所以可交易的事件窗从 103 起算,到 104 只有 0.009709。两段都要写出来。写成一根「事件收益」而不说是哪一个价格,会把跳空记进持仓。
- 估计窗。 正常收益用公告之前的五个交易日。市场模型带截距:R = a + b Rₘ + e。估计结果是截距 0.002000、斜率 1.035294。公告日不在这五天里。
- 事后窗。 生效收盘 104 之后再到 102,原始收益是 102/104 − 1 = −0.019231。这五天的市场收益没有覆盖这一段,所以这里只有价格变化,没有异常收益。事后窗用来看漂移有没有留在成交之后,不能拿估计窗的 β 去编一个没有市场收益的异常值。
- 异常收益与累计异常收益。 事件日的市场收益是 0.005。正常收益是 0.002000 + 1.035294 × 0.005,异常收益是 0.032824。事件窗只有这一天时,累计异常收益就等于这一天的异常收益。窗口拉长之后才是逐日相加。相加的每一天都要用当天已经实现的市场收益,不能用事后才知道的平均市场。
- 显著性。 估计窗残差是 −0.000353、0.000706、0.002471、−0.003176、0.000353,和为 0。残差平方和除以 T − 2 = 3,标准差是 0.002376。预测这一天时,标准误还要乘上
√(1 + 1/T + ((Rₘ − ¯Rₘ)²) / (Σ (Rₘ − ¯Rₘ)²))
得到 0.002635。0.032824 相对未四舍五入的标准误,t = 12.4575。五天样本里残差只有几十个基点,3.28 个百分点的异常收益当然会得到一个很大的 t。它说明这一天相对这五天很极端,不是 Bernard 与 Thomas 的横截面检验,也不能外推到别的公告。
预测标准误比残差标准差大,是因为正常收益本身是估出来的。只拿残差标准差去除异常收益,会把 t 报得更高。横截面上还要把不同公司、同一天的公告放在一起看标准误,单只股票的 t 代替不了那一步。
6. 漂移为什么会留下:三条机制,三条都不能直接下单¶
盈余公告后漂移是公告之后价格仍沿意外的方向走。跳空已经在开盘完成的那一部分,不属于漂移。文献里反复出现的形成原因可以收成三条。本章不重估它们的幅度。
第一条是反应不足,而且在个人投资者更多的股票上更明显。公告很多,注意力有限,小公司、同一天扎堆公布的盈余,价格走得更慢。这解释的是漂移的存在,不给出你下一季还能拿到的百分点。
第二条是预期粘着。分析师不会在一个晚上把模型改到新盈余上。上面的 1.20 和 1.45 就是同一笔实际盈余 1.50 的两个时点:过期预期制造 0.30 的假意外,公告前最后一版只剩 0.05。粘着若是真的,信号必须用公告之前已经印出来的那一版预期。用公告之后被修订成 1.50 的「一致预期」,意外变成 0,漂移的预测也被删掉了。
第三条是套利限制。异常收益要变成组合收益,还要付买卖价差、冲击、借券费用,并且承受个股残差波动。负意外的一边在论文里是空头。只做多、杠杆上限 1.0 的账户没有这条腿,负 SUE 至多是不持有。不持有并不等于做空,论文里的多空收益因此不能记到这个账户上。费用仍按 M13 的成交额计算。公告日集中换仓时,未扣费的 0.032824 更不能写成净值。
三条可以同时成立。注意力解释谁没交易,粘着解释信号从哪一个预期来,套利限制解释为什么没被立刻抹平。把其中一条写成「所以 PEAD 还在」是越界:McLean 与 Pontiff 说的是发表之后预测变量的平均收益下降,指数纳入从 7.4% 到不到 1% 是这种适应的例子。PEAD 没有一个可以乘上去的固定折扣,也没有被本章证明仍然存在。
7. 九项财务信号加总成 F-Score,加总不是收益¶
Piotroski(2000,Journal of Accounting Research)在高账面市值比的股票里,用九个 0/1 财务信号把后续表现分开。本章只拆这九项怎么打分,不重估他的高分减低分组合。每一项只用已经申报的报表,申报日之前的价格不能提前使用这个分数。
| 组 | 信号 | 记 1 的条件 |
|---|---|---|
| 盈利 | 总资产收益率 | 当期 ROA 为正 |
| 盈利 | 经营现金流 | 经营现金流为正 |
| 盈利 | 盈利变化 | ROA 高于去年同期 |
| 盈利 | 应计 | 经营现金流大于净利润 |
| 杠杆与流动性 | 杠杆 | 长期负债除以资产低于去年 |
| 杠杆与流动性 | 流动比率 | 流动比率高于去年 |
| 杠杆与流动性 | 发股 | 当期没有增发股票 |
| 效率 | 毛利率 | 毛利率高于去年 |
| 效率 | 资产周转 | 资产周转率高于去年 |
九项都是与自己的历史比,不是与行业比。应计这一项记 1,表示利润里现金的成分更高,而不是表示利润更高。发股记 1 表示没有在外融资,融资本身不一定是坏消息,只是这个分数把「没有发行」当成更干净的内部资金。分数是九项之和,从 0 到 9。Piotroski 在高账面市值比的子集里使用它。拿到全市场、不先做价值筛选,就不是他的检验。
教学打分,不是某家公司的报表:ROA 为正、现金流为正、ROA 上升、杠杆下降、流动比率上升、毛利率上升,这六项记 1。现金流低于净利润、当期有增发、周转率下降,这三项记 0。F-Score 是 6。6 只说明九项里过了六项。它不是预期收益,也不能和 SUE 的 2.705926 相加。两个信号若要合成,权重是 M10 的问题,合成之前还要确认它们的公布日都已经过去。
import numpy as np
def seasonal_sue(history: np.ndarray, current: float) -> float:
"""分母只用已经公布的意外。current 不进入标准差。"""
return current / float(history.std(ddof=1))
def event_ar(market: np.ndarray, stock: np.ndarray, mkt_event: float, stk_event: float) -> tuple[float, float, float]:
"""估计窗不含公告日。返回截距、β 和公告日异常收益。"""
x = np.column_stack([np.ones(len(market)), market])
alpha, beta = np.linalg.lstsq(x, stock, rcond=None)[0]
normal = alpha + beta * mkt_event
return float(alpha), float(beta), float(stk_event - normal)
def f_score(flags: dict[str, bool]) -> int:
"""九项各是 0 或 1。缺一项就拒绝,避免用 0 填上还没公布的科目。"""
keys = (
"roa_positive", "cfo_positive", "roa_up", "cfo_gt_roa", "leverage_down",
"current_up", "no_issue", "margin_up", "turnover_up",
)
return sum(int(flags[key]) for key in keys)
history = np.array([0.10, -0.05, 0.20, 0.00])
sue = seasonal_sue(history, 0.30)
alpha, beta, ar = event_ar(
np.array([0.01, -0.02, 0.015, 0.005, -0.01]),
np.array([0.012, -0.018, 0.020, 0.004, -0.008]),
0.005,
0.04,
)
score = f_score({
"roa_positive": True, "cfo_positive": True, "roa_up": True, "cfo_gt_roa": False,
"leverage_down": True, "current_up": True, "no_issue": False,
"margin_up": True, "turnover_up": False,
})
九项的名字对应盈利、杠杆和效率。教学计数是 6。当期意外不进入 history,否则 SUE 会从 2.705926 降到 2.095291。
8. 时点财务数据:报表期间结束,不等于你已经看见¶
利润表上的季度结束日、公司把文件交给交易所或证监会的申报日、数据商把数字放进库的可用日,是三个日期。回测要用的是可用日。可用日晚于期间结束日。用期间结束日去对齐第二天的收益,等于假设报表在季度最后一天下午已经公开。
修订使得同一期间可以有两套数字。第一次申报的每股盈余若是 1.50,后来重述成 1.62,在重述公开之前,信号只能用 1.50。数据库若只保留最新值,把 1.62 填回旧日期,就是财务上的前视,和价格上用未来复权因子改写历史是同一类错误。M18 的总收益因子要求公司行动按除权日向前累加;财务信号要求数字按可用日向后才进入特征。方向相反,原则相同:当天不存在的信息不能进当天的决策。
本仓库的 return_factors 处理的是拆股、股息和分拆现金,不读取利润表,也没有一份时点财务报表。因此 F-Score 和 SUE 在这里只能用手算教学输入。接到外部财务库时,至少留下四列再谈排序:证券、报表期间、可用时间戳、当时的数值。排序键是可用时间戳,不是报表期间。同一期间的重述是新的一行,而不是覆盖旧行。分析师预期另有一列发布时间,必须早于或等于公告时点,不能用公告之后的修订版去填公告之前的行。
季节性随机游走和分析师预期是两种 SUE,不要混成一个字段。
| 季节性随机游走 | 分析师一致预期 | |
|---|---|---|
| 预期从哪来 | 四个季度前已公布的每股盈余 | 公告前最后一版一致预期 |
| 教学分子 | 0.30,来自历史意外的当期 | 1.50−1.45=0.05 |
| 分母 | 更早意外的样本标准差 0.110868 | 可以是预期离差,也可以只看差额 |
| 最容易的前视 | 把当期 0.30 放进 σ,SUE 变成 2.095291 | 用修订后的 1.50,意外变成 0 |
| 过期信息 | 用上一季代替去年同季,把季节当成意外 | 用四个季度前的 1.20,假意外 0.30 |
两种构造可以同时算。它们回答的问题不同:随机游走问「相对去年同季」,一致预期问「相对公告前市场已经听到的数字」。把 0.30 和 0.05 写成同一笔意外的两种算法,只有在 1.20 仍是当时的一致预期时才成立。否则 0.30 只属于随机游走或过期预期,不属于当天的一致预期。
🔴 深入层:申报日晚于成交日¶
9. 回购和内部人¶
公开市场回购的公告是一个时点,实际买回往往拉得很长。回测若在公告之前的收盘买入,用的是当时还没公开的决定。Ikenberry、Lakonishok 与 Vermaelen(1995,Journal of Financial Economics)讨论市场对公开市场回购反应不足。
美国内部人的 Form 4,在 2002 年《萨班斯–奥克斯利法》之后,一般要在交易日后的第二个营业日结束前申报。交易日当天的市场还不知道这份申报。用交易日的收盘去交易「内部人买入」,是前视。能用的是申报已经公开之后的下一档。港股董事权益的披露晚于成交,具体截止以当时的《证券及期货条例》和交易所规则为准,不在这里写成一个固定天数。
McLean 与 Pontiff 的发表后衰减说的是预测变量的平均收益下降,不是把 PEAD 的回测乘上一个固定折扣。指数纳入从 7.4% 到不到 1%,是这种适应的一个例子,不是所有事件的同一个折扣。
10. 中低频多头组合的财报窗口规避与跳空风控¶
M04 的止损假设缺口还没跳过你的价。案例 2 里,甲骨文 2025-09-10 的隔夜是 +32.16%,摩根大通 2020-03-16 的隔夜是 −18.01%。这两段都已经在开盘价里。月度选股若在调仓日之后的 K 个交易日内会碰到财报,开盘可以一次走完止损距离。
三条规则一起用。K 在下面的教学里取 5。
- 新开仓:财报日当天暂缓,权重记 0。窗口内但还不是公告当天,权重折半,并且不超过单名 10%。
- 已持仓:不为了躲开财报去付一趟双边印花税清仓。权重高于
max_position_pct = 10%的部分截到 10%。 - 财报前夕不挂紧贴现价的止损。隔夜假跳空会在开盘把止损扫掉,成交价却是跳空之后的价格。
教学四行:新开仓距财报 0 天、原权重 0.08,动作 defer,权重 0.00。距财报 3 天,动作 halve,权重 0.04。已持仓距财报 2 天、原权重 0.16,动作 hold,权重 0.10。窗口内的止损单,动作 blocked,原权重 0.08 留在账上,不发出这张止损。
# goal8-check
"""财报窗口:新开仓折半或暂缓,已持仓只截单名上限。离线。"""
from decimal import Decimal
CAP = Decimal("0.10")
def earnings_blackout(
intent: str, days_to_report: int, k: int, weight: Decimal
) -> tuple[str, Decimal]:
# 窗口含公告当天。K 是调仓日之后还要看的交易日数。
inside = 0 <= days_to_report <= k
if intent == "stop" and inside:
return "blocked", weight
if intent == "open" and days_to_report == 0:
return "defer", Decimal("0.00")
if intent == "open" and inside:
return "halve", min(weight / 2, CAP)
if intent == "hold":
return "hold", min(weight, CAP)
return "allow", min(weight, CAP)
cases = (
("open", 0, 5, Decimal("0.08")),
("open", 3, 5, Decimal("0.08")),
("hold", 2, 5, Decimal("0.16")),
("stop", 1, 5, Decimal("0.08")),
)
for intent, days, k, weight in cases:
action, out = earnings_blackout(intent, days, k, weight)
print(intent, days, action, f"{out:.2f}")
真实案例¶
100、103、104 和两个 SUE 仍是正文里的手算。下面用文献里已经报告的纳入收益,以及本仓库种子里的真实开盘,看跳空落在哪一个时点。
案例 1:纳入的 7.4% 没有留在近十年¶
Greenwood 与 Sammon(2024,Journal of Finance)报告,标普 500 纳入的平均异常收益在 1990 年代是 7.4%,近十年不到 1%;2010–2020 年剔除约 0.1%。这是他们的样本。指数基金规模变大之后,可预测的冲击没有同步变大。Form 4 在 2002 年之后一般要到交易日后的第二个营业日才公开,内部人的成交日仍然不是信号日。
案例 2:季度里最大的隔夜跳空,开盘已经把它走完¶
每个日历季度取隔夜跳空 |Oₜ/Cₜ₋₁ − 1| 最大、且绝对值至少 3% 的那一天。前收买进、拿到当天收盘,是看见跳空之后才去用前收,属于前视。开盘买进只拿到 Cₜ/Oₜ − 1。再晚一个交易日,拿到的是次日开盘到次日收盘。
甲骨文用 technical_indicators_lab/seed_data/US.ORCL_bars.csv 的未复权开盘和收盘。这样的季度有 62 个,上涨和下跌各 31 个。上涨那 31 天,隔夜平均 +8.72%,前收到收盘平均 +7.45%,开盘到收盘平均 −1.21%,次日开盘到次日收盘平均 +0.05%。下跌那 31 天,隔夜平均 −6.27%,前收到收盘平均 −5.82%,开盘到收盘平均 +0.49%,次日大约是 0。最大的一跳在 2025-09-10:隔夜 +32.16%,日内只有 +2.86%,前收到收盘 +35.95%,次日开盘到收盘是 −6.81%。
摩根大通用 jpm_lab/seed_data/US.JPM_bars.csv。上涨 22 天:隔夜平均 +6.13%,前收到收盘 +7.74%,开盘到收盘 +1.48%,次日 −0.48%。下跌 13 天:隔夜 −5.75%,前收到收盘 −5.89%,开盘到收盘 −0.10%,次日 −0.35%。最大的一跳在 2020-03-16:隔夜 −18.01%,当天开盘之后反弹 +3.71%,前收到收盘 −14.96%,次日开盘到收盘 +3.16%。−18% 在开盘价里,开盘之后的反弹补不回这一段。
momentum_plus_lab/seed_data/adj_close.csv 里的 AAPL 只有复权收盘,拆不开隔夜和日内。47 个日历季度里,收盘到收盘绝对值至少 3% 的极值日,绝对涨跌平均 6.01%,下一天的收盘到收盘平均 +0.13%。最大的一天是 2025-04-09,复权收盘 +15.33%,下一天 −4.24%。这只说明极值日没留到第二天,不能写成开盘已经含了多少。三只股票共同的读法是:能成交的价格在跳空之后。前收到收盘把已经发生的缺口记进了策略。
方法论来源¶
| 概念 / 模型 | 核心文献或规范出处 |
|---|---|
| 事件研究五步 | MacKinlay (1997), Journal of Economic Literature |
| 盈余公告后漂移 | Bernard & Thomas (1989), JAR 27: 1–36 |
| 季节性 SUE | 相对四季之前的盈余;分母只用已公布意外 |
| F-Score | Piotroski (2000);本章教学计数是 6 |
| 指数纳入 | Harris & Gurel (1986), JF;Greenwood & Sammon, JF,2024-12 在线 |
| 时点财务 | 排序用可用时间戳;重述另起一行 |
| 图 | figures/M19/plot_events.py |
| 纳入收益 | Greenwood & Sammon (2024), JF;7.4% 与不到 1% 是论文报告的样本 |
| 跳空时点 | ORCL、JPM 的实验室 K 线;AAPL 只用 adj_close.csv 的复权收盘 |
💡 思考回顾与巩固¶
核心认知纠偏¶
| 误区 | 反例 | 用法 |
|---|---|---|
| 公告日收益就是策略收益 | 从 100 到 104 是 4%,从 103 买到 104 是 0.97% | 成交价在跳空之后 |
| 漂移等于跳空 | 开盘之后到 102 是 −0.97% | 两段分开 |
| SUE 的 σ 用上当期更稳 | 2.705926 变成 2.095291 | 分母只用已公布的意外 |
| 一致预期没有日期 | 1.20 和 1.45 给出 0.30 和 0.05 | 用公告前的最后一版 |
| 公告日可以参加 β 的估计 | β 从 1.035294 升到 1.192344,异常收益从 0.032824 降到 0.026699 | 窗口在公告前结束 |
| 单日 t = 12.4575 是论文里的显著性 | 估计窗只有五天,残差标准差 0.002376 | 这是教学诊断,不是横截面检验 |
| 事后窗 −0.019231 已经是异常收益 | 没有那天的市场收益 | 只记原始价格变化 |
| F-Score 为 6 就是预期收益 | 九项里六项为 1 | 分数不是收益,也不和 SUE 相加 |
| 季度结束日的报表已经公开 | 可用日晚于期间结束日 | 用可用时间戳排序 |
| 重述后的 1.62 可以填回旧日期 | 重述公开前只能用 1.50 | 新行,不覆盖 |
| 负 SUE 可以在只做多账户里做空 | 杠杆上限 1.0,信号不接受负权重 | 空头腿留成现金 |
| 1990 年代的 7.4% 仍是近十年的纳入收益 | 近十年不到 1% | 用 Greenwood 与 Sammon 的后一段 |
| 内部人交易日的收盘可以用 | Form 4 一般在其后第二个营业日 | 用申报公开之后 |
| 发表后衰减等于把 PEAD 减半 | M08 拒绝这个折扣 | 衰减不是一个乘数 |
| 本章的 0.97% 是 Bernard 与 Thomas 的漂移 | 那是 100、103、104 这组教学价 | 论文收益没有重算 |
费曼自测¶
- 为什么从 100 持有到 104 的 4%,不能记在收盘后才看到公告的账户上?
- 把当期意外放进 σ,SUE 为什么变小?
- 公告日进入回归之后,异常收益为什么从 0.032824 降到 0.026699?
- 指数基金规模变大,为什么纳入的平均异常收益反而变小?
自测要点
1. 公告在收盘之后,100 已经不能买。下一笔的价格是 103。4% 里的 3 个点是开盘跳空,不是持仓的收益。 2. 0.30 比历史意外更极端。放进样本之后,标准差从 0.110868 升到 0.143178,分子不变,SUE 从 2.705926 降到 2.095291。这个更大的标准差在公布之前并不知道。 3. 公告日股票收益 0.04 远高于市场 0.005。放进回归会把 β 从 1.035294 抬到 1.192344。用更高的 β 去解释同一天,剩下的异常收益就变小。这一天不该参加自己的基准估计。 4. 纳入一旦被写进很多账户,价格会在生效日之前被提前交易掉。Greenwood 与 Sammon 的近十年不到 1%,对照的是 1990 年代的 7.4%。规模变大没有把冲击同步放大。四维习题¶
A. 概念题¶
- 盈余公告后漂移和公告日跳空,各自由哪一个价格区间衡量?
- 季节性随机游走的预期为什么是 Eₜ₋₄,而不是上一季的 Eₜ₋₁?
- 分析师预期在公告之后被修订,用修订后的数字做 SUE 会怎样?
- Form 4 的申报日和内部人的成交日,哪一天才能进入信号?
B. 计算题¶
- 收盘 100、开盘 103、其后收盘 104 和 102。四段收益:跳空、收盘到 104、开盘到 104、开盘到 102。
- 历史意外 0.10、−0.05、0.20、0.00,当期 0.30。两种 SUE 各是多少?
- 实际 1.50,预期 1.45 和 1.20。两笔意外各是多少?
- 公告前 β 是 1.035294、截距 0.002000。公告日股票 0.04、市场 0.005。异常收益是多少?把公告日放进回归后,异常收益变成多少?
- 估计窗残差标准差 0.002376,预测标准误 0.002635。相对 0.032824 的 t 是多少?104 到 102 的原始收益是多少?九项里六项为 1 时 F-Score 是多少?
C. 编程实战题¶
- 运行
figures/M19/plot_events.py。核对 0.030000、0.040000、0.009709、−0.009709、2.705926、2.095291、0.032824、0.026699。 - 把开盘从 103 改成 100,说明「收盘到生效收盘」和「开盘到生效收盘」为什么变成同一个数。不要把改完的价格写成某家公司的财报。
- 写信号时,预期的时间戳必须早于或等于公告时点,β 的样本必须在公告日之前结束。不要在开发机为了验证事件而下单。
D. 个人实战思考题¶
- 你只能做多。一篇多空 PEAD 论文的收益,哪一条腿你不能下单?
- 指数公司在收盘后宣布纳入,生效日在五天之后。你准备在宣布当晚的收盘价回测买入。错在哪一个时点?
- 董事在周一买入,Form 4 在周三公开。你周一收盘的动量信号要不要把这笔买入算进去?
完整参考答案与解析
**A1.** 跳空是公告前最后一笔能成交的价格,到公告后第一笔能成交的价格。收盘后公告时,就是 100 到 103。漂移从成交价再往前量,教学例子里是 103 到 104,或者 103 到 102。4% 把两段绑在一起,还假装 100 可以买。 **A2.** 季报有季节性。零售的第四季通常高于第三季,用 Eₜ₋₁ 会把季节当成意外。Eₜ₋₄ 用的是去年同一季,而且那个数字在 t 之前已经公布。 **A3.** 修订后的预期已经含有公告里的盈余。实际 1.50 对修订后的 1.50,意外是 0,当天的信号消失。这是用公告之后的信息解释公告之前的仓位。 **A4.** 申报公开之后的下一档。成交日当天,市场还没有这份 Form 4。2002 年之后,申报一般不晚于交易日后的第二个营业日结束。港股董事权益的天数以当时的规则为准,原则同样是申报晚于成交。 **B5.** 跳空 103/100 − 1 = 0.030000。收盘到 104 是 0.040000。开盘到 104 是 104/103 − 1 = 0.009709。开盘到 102 是 102/103 − 1 = −0.009709。 **B6.** 不含当期:σ = 0.110868,SUE = 2.705926。含当期:σ = 0.143178,SUE = 2.095291。 **B7.** 1.50 − 1.45 = 0.05。1.50 − 1.20 = 0.30。0.30 只有在 1.20 仍是公告前最后一版预期时才能用。 **B8.** 0.04 − (0.002000 + 1.035294 × 0.005) = 0.032824。含进公告日之后是 0.026699。β 从 1.035294 变成 1.192344。 **B9.** t = 12.4575,用的是未四舍五入的预测标准误,不是用 0.002635 再除一次。102/104 − 1 = −0.019231,这是原始收益。F-Score 是 6。 **C10–C12.** 价格和盈余是教学输入,不是历史财报。开盘改成 100 时跳空为 0。预期时间戳和 β 窗口是两条独立约束。import numpy as np
history = np.array([0.10, -0.05, 0.20, 0.00])
sigma = history.std(ddof=1)
sue = 0.30 / sigma
sigma_leaky = np.array([0.10, -0.05, 0.20, 0.00, 0.30]).std(ddof=1)
sue_leaky = 0.30 / sigma_leaky
# 估计窗不含公告日。残差标准差用 T-2。
market = np.array([0.01, -0.02, 0.015, 0.005, -0.01])
stock = np.array([0.012, -0.018, 0.020, 0.004, -0.008])
design = np.column_stack([np.ones(5), market])
alpha, beta = np.linalg.lstsq(design, stock, rcond=None)[0]
ar = 0.04 - (alpha + beta * 0.005)
resid = stock - design @ np.array([alpha, beta])
sigma_e = np.sqrt(np.sum(resid**2) / 3)
# 预测标准误还要加上公告日市场点离估计窗均值的距离,见第 5 节的 0.002635。
📌 章节知识总结¶
一句话核心洞察¶
- 收盘后公告时,从 100 到 103 的 3% 不是持仓收益。从 103 到 104 只有 0.97%。
- SUE 的分母只用已经公布的季节性意外。0.30 对 σ = 0.110868 是 2.705926;把当期放进分母变成 2.095291。
- β 用公告前的五日,异常收益是 0.032824。公告日放进回归后变成 0.026699。
- 纳入的平均异常收益从 1990 年代的 7.4% 降到近十年不到 1%。这不是本章教学价格的 4%。
- Form 4 一般在交易日后的第二个营业日。成交日的收盘不能用来交易这笔内部人买入。
- 单日 t = 12.4575 只相对于五天估计窗。事后窗 −0.019231 是原始收益。F-Score 为 6 不是收益。
公式与样本¶
| 对象 | 数值 | 前提 |
|---|---|---|
| 跳空 | 0.030000 | 100 到 103,公告在收盘后 |
| 收盘到生效 | 0.040000 | 假装能用 100 买到 |
| 开盘到生效 | 0.009709 | 103 到 104 |
| 开盘到其后 | −0.009709 | 103 到 102 |
| SUE | 2.705926 | 历史四期,σ = 0.110868,n − 1 |
| 含当期的 SUE | 2.095291 | σ = 0.143178 |
| 预期差 | 0.05 与 0.30 | 1.50 对 1.45 和 1.20 |
| 异常收益 | 0.032824 | β = 1.035294,截距 0.002000 |
| 含公告日 | 0.026699 | β = 1.192344 |
| 事后窗原始收益 | −0.019231 | 104 到 102,无市场收益,不是异常收益 |
| 残差标准差 | 0.002376 | 五天,除以 T − 2 = 3 |
| 预测标准误 | 0.002635 | 含估计窗不确定性 |
| 单日 t | 12.4575 | 只相对这五天,不是论文检验 |
| F-Score | 6 | 九项教学打分,六项为 1 |
| 纳入 | 7.4% 到不到 1% | Greenwood 与 Sammon,本章未重估 |
| 剔除 | 2010–2020 约 0.1% | 同一篇,未重估 |
| Form 4 | 第二个营业日 | 2002 年之后的一般期限 |
跨模块¶
| 结论 | 交给 | 位置 |
|---|---|---|
| 收盘后不能按收盘成交 | 时段 | M18 |
| 纳入收益变薄 | 范式 | M02 |
| 不把发表后衰减当成减半 | 陷阱 | M08 |
| 空头腿和费用 | 换手 | M13 |
| 复算 | 图 | ../figures/M19/plot_events.py |
学习指引¶
概念卡片:SUE 与盈余公告后漂移
- 精读:Bernard 与 Thomas(1989)把漂移和风险溢价放在一起讨论。读的时候把公告日跳空从他们的持有期里分开。原文见 JAR 第 27 卷,页码 1–36。
- 对照:4%、0.97% 和两个 SUE 以脚本为准。7.4% 与不到 1% 以 Greenwood 与 Sammon 的样本为准,不把教学价格说成他们的估计。
- 下一步:M20 若用期权表达事件,权利金的时点同样要在公告之后。事件多空的空头腿仍然受杠杆上限 1.0 约束。
自检清单¶
- 能把 100、103、104、102 算成 3%、4%、0.97% 和 −0.97%
- 能说明 4% 里有 3 个点发生在开盘之前
- 能把 SUE 算成 2.705926,并说明含当期分母时是 2.095291
- 能把异常收益算成 0.032824,并说明公告日不进 β
- 能把 1.50 对 1.45 和 1.20 分成 0.05 和 0.30
- 能说出纳入收益从 7.4% 降到近十年不到 1%,以及 Form 4 晚于成交日
- 能把预测标准误 0.002635 和 t = 12.4575 说成五天样本上的诊断
- 能把 104 到 102 算成 −0.019231,并说明它还不是异常收益
- 能把九项里六项为 1 的 F-Score 写成 6,并且不和 SUE 相加
- 能把报表期间、申报日和可用日分开,重述公开前仍用 1.50
