halo 的技术博客

返回

你一定见过「动量因子」:过去 12 个月涨得最好的股票,未来还会涨。但动量有个尴尬的兄弟——短期反转:把窗口从 12 个月缩到 1 个月,过去一个月涨得最多的股票,下个月反而倾向于跌

这不是矛盾,是同一枚硬币的两面。Jegadeesh(1990)最早系统地发现:在 1 个月这样的短窗口里,截面收益是向的(反转);而到了 6~12 个月,才翻成正的(动量)。问题来了——为什么短窗口是反转、长窗口是动量?这篇文章给你一个最干净的解释框架:把日收益拆成「隔夜」和「日内」两部分

一、日收益的两副面孔:隔夜 vs 日内#

任何一天的收益都可以写成:

rday=(closet1opent)隔夜+(opentcloset)日内r_{day} = \underbrace{(close_{t-1} \to open_t)}_{\text{隔夜}} + \underbrace{(open_t \to close_t)}_{\text{日内}}

Birru(2018)在 “Day and Night” 里用美股数据做了个惊人的拆解:把过去 1 个月的收益分成「隔夜累计」和「日内累计」两部分,分别拿去预测下个月收益,结果是:

  • 隔夜部分:预测能力是反转(过去隔夜涨得多→下月跌)
  • 日内部分:预测能力是动量(过去日内涨得多→下月继续涨)

为什么会这样?直觉很朴素:

  • 隔夜是收盘后到开盘前发生的,对散户来说是「想了一晚上、开盘挂单」的时段,充满了情绪化的过度反应——涨了大家更乐观、追涨,跌了更恐慌、杀跌。这种情绪是均值回复的:今天的过度乐观,明天就被现实拉回。
  • 日内是交易时段的真实博弈,承载的是信息慢慢被消化、被定价的持久过程——好消息的影响会延续几天甚至几周。

一句话:隔夜是噪音+过度反应(会回吐),日内是信息+趋势(会延续)

二、合成面板:把「过度反应」与「信息」分开建模#

为了把上面的机制跑通且可复现,我们造一个简单的合成面板:400 只股票 × 120 个月(10 年)。每只股票有两个隐藏的月度成分:

  1. R 成分(过度反应,放在隔夜):一阶自回归 Rm=ϕRRm1+εRR_m = \phi_R R_{m-1} + \varepsilon_R,取 ϕR=0.60\phi_R = -0.60——负自相关,意味着这个月隔夜涨多了,下个月会回吐。这是反转的来源。
  2. P 成分(真实信息,放在日内):一阶自回归 Pm=ϕPPm1+εPP_m = \phi_P P_{m-1} + \varepsilon_P,取 ϕP=0.92\phi_P = 0.92——强正自相关,信息缓慢释放、延续多个月。这是动量的来源。

每天把当月成分均摊到 21 个交易日,加上日度特异噪声;月末把 21 天求和得到月度收益。

跑完后我们验证两个成分的自相关:R 的一阶自相关约 −0.60(确实在反向均值回复),P 的一阶自相关约 +0.92(确实在缓慢延续)。机制按设计成立。

三、十分位:隔夜反转严格单调#

按「过去 1 个月隔夜收益」排序分成十组,看下个月这十组的平均收益

def decile_ls(sig, fut):
    dec_ret = np.zeros((10, M-1))
    for m in range(M-1):
        order = np.argsort(sig[:, m]); ranks = np.empty(N); ranks[order] = np.arange(N)
        d = np.clip(ranks // (N//10), 0, 9)
        for k in range(10):
            dec_ret[k, m] = fut[d == k, m+1].mean()
    return dec_ret, dec_ret[-1, :] - dec_ret[0, :]   # D10 - D1

dec_on, ls_on_raw = decile_ls(ret_on_m, ret_total_m)
ls_on = -ls_on_raw   # 买输家(D1)卖赢家(D10): 多空转正
python

短期反转:按过去 1 月「隔夜收益」排序的十分位下月平均收益

结果非常干净:从 D1(最大隔夜输家)到 D10(最大隔夜赢家),下月平均收益严格单调递减——过去隔夜涨得越狠,下个月跌得越狠。买输家卖赢家,多空年化约 22%(合成 Sharpe 虚高,后文会说),回撤近乎 0。这正是「隔夜过度反应→下月均值回复」的直接证据。

四、三类策略同台:隔夜反转 vs 日内动量#

把三个截面信号放在一起对比:

策略信号(过去 1 月)方向多空年化(合成)
隔夜反转隔夜收益买输家卖赢家~22%
日内动量日内收益买赢家卖输家~3.4%
总收益反转总收益买输家卖赢家~4.8%
dec_int, ls_int = decile_ls(ret_intra_m, ret_total_m)   # 日内动量(买赢家)
dec_tot, ls_tot_raw = decile_ls(ret_total_m, ret_total_m); ls_tot = -ls_tot_raw
python

隔夜反转 / 总反转 / 日内动量:累计净值对比

关键观察:隔夜反转(22%)远比日内动量(3.4%)和总反转(4.8%)强劲。为什么?因为「总收益」把隔夜(反转)和日内(动量)混在一起,两者部分抵消,信号被稀释;而单独把隔夜成分拎出来,反转信号最纯。这也给实战一个洞见:与其用模糊的「过去 1 月总收益」做反转,不如把隔夜那段单独拿出来——信号更干净

五、rank-IC 随持有期:反转→动量的经典 flip#

短期反转研究里最漂亮的实证,是「信号有效期」随持有期翻转。我们用 rank-IC(信号与下期收益的秩相关)扫一遍持有期:

from scipy.stats import rankdata
def rank_ic(sig_hist, fut, h):
    ics = []
    for m in range(h, M-1):
        s = sig_hist[:, m-h+1:m+1].sum(1)   # 过去 h 月累计
        f = fut[:, m+1]
        ics.append(np.corrcoef(rankdata(s), rankdata(f))[0, 1])
    return np.mean(ics)

ic_1m  = rank_ic(ret_total_m, ret_total_m, 1)    # -0.058  反转
ic_3m  = rank_ic(ret_total_m, ret_total_m, 3)    # +0.14   翻正
ic_6m  = rank_ic(ret_total_m, ret_total_m, 6)    # +0.21   动量
ic_12m = rank_ic(ret_total_m, ret_total_m, 12)   # +0.21   动量
python

rank-IC 随持有期:短期限反转(负) → 中期限动量(正)

实测:1 个月 IC = −0.058(反转),3 个月翻正到 +0.14,6~12 个月稳定在 +0.21(动量)。这条「短负长正」的曲线,就是 Jegadeesh 反转与动量同宗同源最直观的证据——反转是动量在短窗口的「回声」:你这个月涨多了,下个月先回吐一点(反转),但中期趋势(信息)最后还是赢(动量)。

六、成分拆解:隔夜是过度反应,日内是信息#

最后用自相关把「拆解」做扎实。我们直接算两个隐藏成分的一阶月自相关:

R_autocorr = np.mean([np.corrcoef(R[:, m], R[:, m+1])[0,1] for m in range(M-1)])  # -0.60
P_autocorr = np.mean([np.corrcoef(P[:, m], P[:, m+1])[0,1] for m in range(M-1)])  # +0.92
python

日收益分解:隔夜含均值回复冲击(负自相关),日内含持久信息(正自相关)

R 自相关 −0.60,P 自相关 +0.92——隔夜成分在反向均值回复(过度反应被修正),日内成分在缓慢延续(信息被消化)。这从数据层面坐实了第一节的直觉框架,而不是只靠嘴说。

七、六类真实陷阱(实战必看)#

  1. 合成 Sharpe 虚高:本文隔夜反转多空 Sharpe 是合成设定下 400 只股票、十分位成批平均后的结果,把特异风险几乎全平均掉了;实盘 1 月反转多空 Sharpe 通常只有 0.3–0.8,不是 15。数字证明方向正确,不证明收益真实。
  2. 幸存者偏差:合成面板没有退市、没有流动性约束。实盘里「过去一个月涨最多的」往往混着一堆小盘妖股,做空它们的借券成本、冲击成本会把反转利润吃光甚至反亏。
  3. 做空约束:短期反转本质是「买输家、卖赢家」,靠做空赢家赚钱。A 股做空受限(融券难、成本高),纯多头版只能买输家、放弃卖赢家,收益砍半。
  4. 换手率爆炸:每月(甚至每周)重新排序意味着极高换手,单边 0.1% 成本下 1 月反转常被摩擦吃掉大半。要不做持有期加权、要不拉长信号窗口。
  5. 微观结构噪声:隔夜收益里固然有过度反应,但也有大量「盘后大宗 / 指数调仓 / 跳空」的纯噪声,不是所有隔夜波动都等于「情绪」。需要剔除异常跳空再算信号。
  6. 中美结构不同:Birru 的「隔夜反转 / 日内动量」结论建立在美股 T+0、散户主导盘前盘后情绪的市场上。A 股的隔夜窗口(15:00→9:30)夹着一个无法交易的漫长夜,且政策市特征更强,直接套隔夜收益排序常失效——更稳的做法是「今开 vs 昨收」的跳空 + 日内强弱合成。

八、小结:反转与动量,本是一体#

把这篇文章和「时间序列动量」「因子动量」放在一起看,会得到一条清晰的逻辑链:

  • 日内(信息)成分 → 中长期动量(6~12 月为正)
  • 隔夜(过度反应)成分 → 短期反转(1 月为负)

所谓「短期反转因子」,本质上就是剥离了隔夜情绪噪音后,捡日内趋势还没完全定价的那一段均值回复。它不是动量的对立面,而是动量在最短窗口上的「回声效应」。

对你做策略的意义:别再笼统地说「追涨杀跌」或「低买高卖」。先把收益拆进隔夜和日内,你会发现——隔夜该反向、日内该顺势,把两者分开建模,比用一个模糊的「过去 N 日收益」信号干净得多。


代码与图表均由自包含 Python(numpy/scipy)真实计算,随机种子固定为 20260717,可完整复现。所有统计数字(含十分位收益、rank-IC、自相关)来自文中脚本输出。合成 Sharpe 含成批平均带来的虚高,实战请以真实成本与做空约束重新估计。

短期反转因子:用隔夜过度反应捡日内均值回归的钱
https://blog.halo26812.eu.org/blog/short-term-reversal-factor
Author halo
Published at 2026年7月17日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨