QuantWeft量化经纬
English
本章小节

第 25 章/ 45

动量(Momentum)

主线:把“过去赢家继续赢”写成端点精确的窗口、方向明确的持仓和成本可计的收益,并区分横截面动量(cross-sectional momentum, XSMOM)与时间序列动量(time-series momentum, TSMOM,又称趋势 trend)是两件不同的事。 先修:第11章(收益与复利)、第23章(截面研究)。完成后你能:写出 12–1 动量在信号月的确切端点,不差一个月;说明 ∏(1+r)−1\prod(1+r)-1 与求和的区别;构造横截面赢家减输家与时间序列趋势两种组合;用重叠持有降低换手并推出换手公式;识别价格拆股、期货展期与连续合约(continuous contract)、幸存样本三类数据口径问题;用 10 个行业的真实月收益报告结果,并把负结果、成本与崩溃状态一起报告。

1. 本章目标

读者应能做到:

  1. 对信号月 ss(月末),写出 12–1 的形成区间(months s−11,…,s−1s-11,\dots,s-1,共 11 个月)、被跳过的月份(ss)、持有区间(s+1s+1),并说明时间序列动量(Moskowitz、Ooi 与 Pedersen 2012,下称 MOP)的 12–0 窗口含最近一个月。
  2. 手算并区分 ∏(1+r)−1\prod(1+r)-1、∑r\sum r、∑ln⁡(1+r)\sum\ln(1+r),给出一个求和排序与复利排序相反的例子。
  3. 构造横截面动量组合(形成收益最高桶做多、最低桶做空)与时间序列动量组合(各资产按自己过去收益的符号做多或做空,按事前波动率缩放),并分别说明净暴露(net exposure,有符号权重之和,亦即多头权重和减空头权重绝对值之和)、总暴露(gross exposure,权重绝对值之和)与基准。
  4. 构造 KK 个月重叠持有(overlapping holdings)的组合,推出相邻月份权重变化为 (w(t)−w(t−K))/K(w^{(t)}-w^{(t-K)})/K,并用真实行业数据比较 K=1,3,6K=1,3,6 的换手(turnover,每月权重绝对变化之和)与成本。
  5. 识别并修复三类口径错误:拆股与分红造成的假输家、期货展期拼接造成的假收益、以现存股票池回测造成的幸存者偏差。
  6. 报告 10 个行业组合上的 12–1、窗口变体、J×KJ\times K 网格(含 Bonferroni 门槛)、时间序列趋势与“始终做多”基准、崩溃状态,并明确哪些不能说。

完成标准:任何动量信号都附一张时间图,标明形成月份、跳过月份、signal_time、trade_time 与持有月份,并能说明窗口比标准定义多一个月或少一个月会改变什么。

2. 为什么 Quant 需要它

动量是 Part IV 里最容易被“实现错”的因子:定义只有一句话(过去涨得多的继续涨),但窗口端点差一个月、收益用求和代替复利、价格没有调整拆股、期货价格直接拼接、股票池只含现存公司,都会改变结果,而且每一种都不会报错。动量也是有明确崩溃风险的因子:长期均值为正的策略会在极少数月份出现非常大的损失,这些月份集中在市场暴跌之后的反弹里,所以只报告均值与 tt 是不完整的。

本章的逻辑链:先把窗口端点与复利写精确(6.1–6.2),再用重叠持有与波动率缩放写出两种组合(6.3–6.4),再说明为什么两者的风险与基准不同(6.5),再看崩溃状态(6.6),再处理数据口径(6.7–6.8),最后在 10 个行业组合上做完整报告(11 节)。第23章的逐期 IC、分位组合与按依赖结构选择的时间推断方法在这里使用;第22章的试验账本约束窗口搜索;第27章会把展期问题扩展到 carry。

3. 直觉

过去一年涨得最多的资产,下个月比过去一年跌得最多的资产平均多涨一点,这是横截面动量。一条价格自己过去一年向上,下个月继续向上的可能性更大,这是时间序列动量,也叫趋势。前者比较资产之间的相对位置,永远一多一空、净头寸约为零;后者只看每个资产自己,若大多数资产都在上涨,组合就会净做多,收益里混着市场整体的涨跌。

直觉里还有两个细节。其一,为什么要跳过最近一个月:个股的月收益有短期反转,最近一个月的赢家下个月往往回吐一点,这与流动性和微观结构有关(AMP 2013,印刷页 936);所以 12–1 的“1”就是把最近一个月从形成期里拿掉。其二,为什么要看“持有多久”:信号每个月更新,如果每个月都把整个组合换掉,换手很高;让每个月的新持仓批次(vintage) 只占目标账本的 1/K1/K;替换的是 vintage 份额,实际换手还取决于新旧权重差,代价是反应更慢(Jegadeesh 与 Titman 1993 的重叠持有设计)。

简化的边界:本章的真实案例只有 10 个行业组合(已修订的价值加权月收益),它们没有个股的微观结构反转,截面小,且不是可交易的成交序列;个股层面的结论本章没有数据支持。

3.1 让实际月份把 12–1 的时钟固定下来

4. 数学基础

记号。 资产 ii 的月简单收益 Ri,tR_{i,t}(无量纲小数,月 tt 当月的收益,在月末 tt 确定)。信号月 ss 指月末 ss,决策在 ss 月末做出,持有月为 s+1s+1。窗口用“跨度–跳过”记法:12–1 表示向前数 12 个月、跳过最近 1 个月,所以实际使用 12−1=1112-1=11 个月的收益。

复利收益。 对月份集合 W\mathcal W,F=∏k∈W(1+Ri,k)−1F=\prod_{k\in\mathcal W}(1+R_{i,k})-1。展开 ∏k(1+Rk)−1=∑kRk+∑j<kRjRk+⋯+∏kRk\prod_{k}(1+R_k)-1=\sum_kR_k+\sum_{j<k}R_jR_k+\dots+\prod_kR_k,求和只是第一阶;每月收益 ±5%\pm5\% 量级时,二阶项在一年内约为 0.5×(∑R)2−0.5∑R20.5\times(\sum R)^2-0.5\sum R^2 的量级。对数收益 ln⁡(1+R)\ln(1+R) 可以相加:ln⁡(1+F)=∑kln⁡(1+Rk)\ln(1+F)=\sum_k\ln(1+R_k)。

时间序列信号。 资产 ii 的符号 sign(Fi)∈{−1,0,+1}\mathrm{sign}(F_i)\in\{-1,0,+1\};波动率 σi,s\sigma_{i,s} 是只用 ss 月末及之前的收益估计的年化标准差;组合权重 wi,s=sign(Fi,s)⋅(σ∗/σi,s)/Ssw_{i,s}=\mathrm{sign}(F_{i,s})\cdot(\sigma^*/\sigma_{i,s})/S_s,其中 σ∗\sigma^* 是每个资产的目标年化波动率,SsS_s 是当月有效资产数。

重叠持有。 每月形成一个“vintage”(一批目标权重)w(s)∈RNw^{(s)}\in\mathbb R^N,持有 KK 个月;ss 月末的组合为 Bs=1K∑j=0K−1w(s−j)B_s=\frac1K\sum_{j=0}^{K-1}w^{(s-j)}。

熊市状态。 Is=1{∏k=s−23s(1+Mk)−1<0}I_s=\mathbf 1\{\prod_{k=s-23}^{s}(1+M_k)-1<0\},MkM_k 是市场总收益;它只用 ss 月末及之前 24 个月。这是 Daniel–Moskowitz(2014 年工作稿,表 3 说明)的定义。

频率约定:月频;signal_time 是月末;成交在下一个可交易时点;收益区间是 s+1s+1 月;全部权重按月更新。

5. 正式定义

形成区间(formation interval)、跳月(skip)与持有区间(holding interval)。 形成区间是用来计算信号的月份,12–1 是 s−11,…,s−1s-11,\dots,s-1;跳月是形成区间与持有区间之间被排除的月份,12–1 里是月份 ss;持有区间是信号决定后持有的月份,月份 s+1s+1。French 数据库的“prior (2–12)”对应同一定义:组合在 t−1t-1 月末形成、持有 tt 月,形成月份是 t−12,…,t−2t-12,\dots,t-2(French 动量因子说明页)。

横截面动量(cross-sectional momentum)。 同一日期把资产按形成收益排序,买入最高桶、卖出最低桶(Jegadeesh 与 Titman 1993 用十分位)。它按相对排名定方向,而非按每个资产绝对收益的正负;组合市场风险仍取决于两腿暴露。

时间序列动量(time-series momentum, TSMOM)。 每个资产按自己过去收益的符号决定多空,位置大小反比于事前波动率(Moskowitz、Ooi 与 Pedersen 2012,式 (5):sign(rt−12,t)⋅(40%/σt) rt,t+1\mathrm{sign}(r_{t-12,t})\cdot(40\%/\sigma_{t})\,r_{t,t+1},其中 σt\sigma_t 是 tt 月末已知的事前波动率;形成窗口含最近一个月)。

短期反转(short-term reversal)。 最近一个月收益的负向预测,在个股中与流动性和微观结构有关(AMP 2013);本章在行业组合里检验它是否存在。

趋势(trend)。 与时间序列动量同义使用,指资产价格自身的持续方向。

动量崩溃(momentum crash)。 动量策略出现的持续或极端的负收益,集中在市场下跌之后的反弹月份(Daniel 与 Moskowitz 2014)。

再平衡(rebalancing)与重叠持有。 每月更新信号并调整头寸;重叠持有每月替换一个占 1/K1/K 的 vintage,不保证交易量恰降为 1/K1/K。本课每月重置这些目标权重的等权平均,忽略价格漂移;它不同于不调整的真实买入持有。

总回报、价格回报与调整价格。 价格回报只含价格变动;总回报含股息与拆股调整。拆股后未调整的价格会把拆股当作暴跌。

展期(roll)与连续合约。 期货合约到期前换到下一个合约。把不同合约的价格首尾相连得到的“连续价格”在展期日有一个跳跃,这个跳跃不是持有任何合约的收益。

幸存者偏差(survivorship bias)。 回测的股票池只包含后来仍然存在的公司,而在每个历史 signal_time 实际可投资的公司(含后来退市者)没有被包含。

6. 推导

6.1 12–1 的精确端点

信号月 ss(月末)。形成收益

Fi,s=∏k=111(1+Ri,s−k)−1, F_{i,s}=\prod_{k=1}^{11}(1+R_{i,s-k})-1 ,

所用月份是 s−11,s−10,…,s−1s-11,s-10,\dots,s-1,共 11 个月;月份 ss 被跳过;持有月份是 s+1s+1。向前数 12 个月(s−11s-11 到 ss)、拿掉最近 1 个月,所以叫 12–1。MOP(2012)的时间序列信号用 rt−12,tr_{t-12,t},即 ∏k=011(1+Rs−k)−1\prod_{k=0}^{11}(1+R_{s-k})-1,月份 s−11,…,ss-11,\dots,s,共 12 个月,不跳月。两者窗口相差一个月,不是同一个信号。差一个月的三种错误:(1) 窗口向前挪一个月(s−12,…,s−2s-12,\dots,s-2),丢掉最近的有效信息;(2) 窗口向后挪一个月(s−10,…,ss-10,\dots,s),把应跳过的月份包含进来;(3) 12–1 误写成 12 个月减 1 个月后仍用 12 个月(s−12,…,s−1s-12,\dots,s-1)。formation_return(returns, window=11, skip=1) 的第 ss 行恰是 s−11,…,s−1s-11,\dots,s-1,并由测试验证改变月份 ss 及之后的收益不会改变该行(第23章第9节代码段五)。

6.2 为什么是 ∏(1+r)−1\prod(1+r)-1,不是 ∑r\sum r

收益在时间上复利:本金 1 经过 nn 个月变为 ∏(1+Rk)\prod(1+R_k),累计收益是它减 1。求和低估或高估取决于波动:∏(1+Rk)−1=∑Rk+∑j<kRjRk+…\prod(1+R_k)-1=\sum R_k+\sum_{j<k}R_jR_k+\dots。对排序的影响:小收益二阶近似下,固定算术和时更大的平方和降低复利;严格比较需额外条件,例如同样正财富因子的均值保持扩散使对数和下降。任意多月路径仅凭方差大小不能推出复利总序。下面两个月例可直接精确计算。例:X 的两个月收益 +50%,−50%+50\%,-50\%,求和 0,复利 1.5×0.5−1=−25%1.5\times0.5-1=-25\%;Y 的两个月收益 0,00,0,求和 0,复利 0。用求和排序,X 与 Y 并列;用复利排序,Y 高于 X。对数收益求和与复利一致:ln⁡1.5+ln⁡0.5=−0.288=ln⁡0.75\ln1.5+\ln0.5=-0.288=\ln0.75。所以本章的信号一律用复利;若数据本身是对数收益,则用求和后再取指数(formation_return 对简单收益取 log1p 求和后 expm1,收益单元缺失或历史不足使窗口为 NaN;缺日历月行违反连续索引要求并报错)。

6.3 重叠持有组合的换手

每月形成一批新权重 w(s)w^{(s)},持有 KK 个月,组合 Bs=1K∑j=0K−1w(s−j)B_s=\frac1K\sum_{j=0}^{K-1}w^{(s-j)}。相邻月份的组合差

Bs−Bs−1=1K(w(s)−w(s−K)) B_s-B_{s-1}=\frac1K\bigl(w^{(s)}-w^{(s-K)}\bigr)

因为 BsB_s 与 Bs−1B_{s-1} 共有中间 K−1K-1 个 vintage,只相差最新加入的 w(s)w^{(s)} 与刚被移出的 w(s−K)w^{(s-K)}。所以目标权重换手

turns=∑i∣Bi,s−Bi,s−1∣=1K∥w(s)−w(s−K)∥1. \mathrm{turn}_s=\sum_i|B_{i,s}-B_{i,s-1}|=\frac1K\bigl\|w^{(s)}-w^{(s-K)}\bigr\|_1 .

每个 vintage 的总暴露为 2(各腿 1),所以 ∥w(s)−w(s−K)∥1≤4\|w^{(s)}-w^{(s-K)}\|_1\le4,换手上界 4/K4/K;K=1K=1 时是相邻两个月的变化。信号越持续,∥w(s)−w(s−K)∥1\|w^{(s)}-w^{(s-K)}\|_1 随 KK 增长得越慢,换手降低得越多。真实行业数据上 K=1,3,6K=1,3,6 的换手均值是 0.904、0.518、0.364(11 节):K=3K=3 时 ∥⋅∥1\|\cdot\|_1 均值约 3×0.518=1.553\times0.518=1.55,大于 K=1K=1 的 0.904,说明三个月前的组合比一个月前的更不同,但每月只需修订三分之一。代价是信号反应更慢:持有 KK 个月使组合里平均有 (K−1)/2(K-1)/2 个月的陈旧信息。

6.4 时间序列动量的暴露与基准

权重 wi,s=sign(Fi,s) (σ∗/σi,s)/Ssw_{i,s}=\mathrm{sign}(F_{i,s})\,(\sigma^*/\sigma_{i,s})/S_s,总暴露 ∑∣wi∣=σ∗S∑i∣sign(Fi)∣/σi\sum|w_i|=\frac{\sigma^*}{S}\sum_i|\mathrm{sign}(F_i)|/\sigma_i ;只有所有有效符号非零时才能简写为倒数波动率之和,零信号权重为零;净暴露 ∑wi=σ∗S∑isigni/σi\sum w_i=\frac{\sigma^*}{S}\sum_i\mathrm{sign}_i/\sigma_i,由多空资产的占比与波动率共同决定。手算(8.3 节):三个资产,信号 (+1,−1,+1)(+1,-1,+1),波动率 (10%,20%,40%)(10\%,20\%,40\%),σ∗=40%\sigma^*=40\%:每个资产单独的仓位 (4,−2,1)(4,-2,1),除以 3 得 (1.333,−0.667,0.333)(1.333,-0.667,0.333),总暴露 2.333,净暴露 1.0。净暴露说明名义多空不平衡,但不单独证明市场 beta 的大小或符号;市场 beta 还需各资产暴露。这个行业样本的多数资产有股票市场风险:多数资产在趋势向上时,组合净做多。所以评价时序动量必须与“始终做多、同样缩放”的基准比较:rs+1TS=∑iwi,sri,s+1r^{\mathrm{TS}}_{s+1}=\sum_i w_{i,s}r_{i,s+1} 对 rs+1AL=∑i(σ∗/σi,s/S) ri,s+1r^{\mathrm{AL}}_{s+1}=\sum_i(\sigma^*/\sigma_{i,s}/S)\,r_{i,s+1}。MOP(2012)也做了对始终做多的回归(“a positive alpha in 90% of the cases”)。真实行业数据上,时间序列 12–0 的月均 1.391%,始终做多(同样缩放)1.779%;两者差 −0.389%-0.389\%(t=−1.05t=-1.05):这个样本里趋势过滤没有超过始终持有。

6.5 横截面与时间序列的差别

横截面动量是相对的:多空等额,净暴露为零,名义资金配平;市场 beta 是 ∑iwiβi\sum_iw_i\beta_i,并不因 ∑iwi=0\sum_iw_i=0 而必为零。它主要表达相对强弱,仍可能承担市场方向风险。时间序列动量是绝对的:每个资产只与自己的过去比较,组合可以全部做多或全部做空,净暴露随市场状态变化。两者的信号都用过去收益,但组合性质不同,所以它们的相关只有 0.42(行业样本),评价时的基准也不同:横截面对零(或对等权),时间序列对始终做多。

6.6 崩溃状态:条件均值

把月份按信号月的状态分成熊市与非熊市(过去 24 个月市场累计收益是否为负),横截面动量的下月多空收益的条件均值为 rˉ1\bar r_1 与 rˉ0\bar r_0,状态占比 pp,则无条件均值 =prˉ1+(1−p)rˉ0=p\bar r_1+(1-p)\bar r_0。行业数据:熊市状态占 16.2%(191 个月),均值 −0.179%-0.179\% 每月(标准差 7.05%);非熊市 987 个月均值 +0.644%+0.644\%(标准差 4.37%)。熊市状态下波动更高、均值为负,十个最差月份里 7 个出现在熊市状态之后,其中 8 个月市场当月上涨(11 节表)。这与 Daniel–Moskowitz(2014,印刷页 8)对个股赢家减输家的描述方向一致,但本章样本是 10 个行业组合,不是该文的十分位个股,不能当作复现。

6.7 价格、拆股、分红与总回报

拆股比例 ρ\rho(1 股变 ρ\rho 股)、拆股后每股价格 PpostP^{\mathrm{post}}、每新股现金红利 DD、拆股前价格 PpreP^{\mathrm{pre}}:总回报 R=(ρPpost+ρD)/Ppre−1R=(\rho P^{\mathrm{post}}+\rho D)/P^{\mathrm{pre}}-1;未调整的价格变动是 Ppost/Ppre−1P^{\mathrm{post}}/P^{\mathrm{pre}}-1。第3章的构造例:Ppre=100P^{\mathrm{pre}}=100,ρ=2\rho=2,Ppost=51P^{\mathrm{post}}=51,D=1D=1:总回报 (102+2)/100−1=+4%(102+2)/100-1=+4\%,未调整价格变动 −49%-49\%。用未调整价格的动量会把这只股票当成输家;这一个月的收益被低估 4−(−49)=534-(-49)=53 个百分点;整段形成信号的差还要乘以窗口中其余月份的财富因子(见下文),不能直接当作49个百分点。

6.8 展期拼接:连续价格不是收益

本节及8.5、25-E06将同一合约的价格比串联为收益指数:忽略费用和抵押品利息,每段以起始合约价全额抵押,并随财富调整名义金额,才可将各段财富因子相乘。固定一手合约须逐段计算货币P&L,再按明确资本分母换算,不能直接称相同资本回报。

合约 A 在 t0,t1t_0,t_1 的价格 100,102100,102;到 t1t_1 展期,下一个合约 B 在 t1,t2t_1,t_2 的价格 106,107106,107。把价格首尾相连:100,102,106,107100,102,106,107,“收益” +2%, +3.92%, +0.94%+2\%,\,+3.92\%,\,+0.94\%。第二个 +3.92%+3.92\% 是 106/102−1106/102-1,它比较的是两个不同合约的价格:A 在 t1t_1 平仓得 102,同一时刻 B 在 t1t_1 建仓价 106,持有人没有赚到这 3.92%。真实可得的收益是 A 持有期 +2%+2\%、B 持有期 107/106−1=+0.94%107/106-1=+0.94\%。若 B 的价格低于 A(现货溢价、期货曲线向下倾斜),拼接产生负的假收益。AMP(2013,附录 A.5)对商品期货的做法是取最流动合约的日超额收益再复利,而不是拼接价格。时间序列动量在这样的价格上会把展期跳跃当成趋势;第27章把展期收益单独分解。

7. 金融解释

动量的收益来自哪里,本章不判决。 Jegadeesh 与 Titman(1993)报告了美国个股 1965–1989 年的赢家减输家收益;Asness、Moskowitz 与 Pedersen(2013)在多个资产类别里使用同一个简单度量;Moskowitz 等(2012)报告时间序列动量在 58 个期货上持续 1 到 12 个月并部分反转。这些文献里的解释(对信息的反应不足、风险补偿等)本章没有读原文细节,正文不转述;对 Quant 的要求是:无论解释如何,信号必须在 signal_time 可构造,成本必须计入,崩溃必须单独报告。

行业动量与个股动量不同。 12–1 的“跳月”在个股里是为了避开短期反转(AMP 2013,印刷页 936),AMP 同时说明对于股指期货和货币,不跳过最近一个月的动量更强(印刷页 937)。行业组合是大量股票的价值加权平均,买卖价差与流动性的微观结构影响被平均掉。本章的行业数据与这一点一致:窗口 1–0(上个月收益)的多空月均 +0.740%+0.740\%(t=5.61t=5.61),而 1–1(历史规格名;window=1, skip=1,只用 s−1s-1 月)月均 −0.028%-0.028\%(t=−0.21t=-0.21);12–0 的 0.621%0.621\% 大于 12–1 的 0.519%0.519\%。这不说明个股动量也不该跳月,只说明在 10 个行业组合上没有看到跳月带来的好处。

换手是动量的主要成本来源。 横截面 12–1 每月目标权重换手 0.904(毛暴露 2);按每单位换手 10 bp 的假设,月成本 9 bp,使 0.519% 变成 0.427%。K=3K=3 把换手降到 0.518,净收益 0.423%,tt 从 2.99 升到 3.06。成本假设是 10 bp 每单位,不是对行业组合的真实估计;卖空的借券与融资成本没有计入。

崩溃风险要和均值一起报告。 10 个行业横截面 12–1 的复利累计在最差时期的回撤约 64%,十个最差月份损失 13.6% 至 34.8%,7 个发生在熊市状态之后。均值为正的策略仍可能让投资者无法持有它。

时间序列趋势的收益里有市场。 行业样本的多数月份趋势信号为正(67.4% 的资产多头),组合平均净暴露 1.05,所以它的月均 1.391% 里包含了股票风险溢价;相对始终做多的差均值为-0.389%每月,t=-1.05,未见统计明确的正增量;这不是回归alpha估计。

7.1 三个问题与三个待检验机制

横截面动量问:“同一天过去赢家相对输家,下月是否更好?”它需要一个共同股票池,做赢家减输家;即使所有股票过去都跌,只要相对排名可用,仍会同时做多和做空。时间序列动量问:“这一个资产自己的过去方向,下月是否延续?”它以零收益或明确的超额收益零点作阈值,所有资产同向时可以净多或净空。短期反转问:“最近一个月的赢家是否回吐?”若信号定为最近月收益的相反数,越大代表最近跌得更多;它与12–1排除了最近月是两种规格,不能用一个名字替换另一个。

反应不足是一种假说:信息进入价格需要时间,使过去趋势继续。它可用事前界定的信息事件和后续漂移检验。风险补偿是另一种:赢家与输家的状态暴露不同,收益伴随某些反弹时的损失;第11节的崩溃状态描述只能提示这种检验,不能证明原因。测量代理还可能来自行业趋势、滞后成交或流动性,须用相应股票池与可得报价控制来检查。三者都能与正历史收益共存,历史利润不能裁决解释。此处提出研究问题,不扩展原文经验结论。

跳月是一项事前规格,代价是放弃最近信息;它是否有益取决于资产与数据口径。行业组合中1–0为正的旧结果和时间序列相对始终做多的负差结果都保留。样本换成个股、期货或货币后,必须重新定义输入,不能按本章19个变体里表现最好者反过来选择主规格。

8. 数值例题:手算每一步

所有数字由 hand_examples() 产生,并由 qa/a5/claude/recompute/ch25_recompute.py 用不同实现复核。例子为教学构造。

8.1 12–1 的窗口:十四个月的收益

某资产 14 个月的月简单收益(月份 1 到 14):0.03,−0.02,0.05,0.01,−0.04,0.06,0.02,−0.01,0.04,0.03,−0.02,0.05,−0.06,0.020.03,\allowbreak -0.02,\allowbreak 0.05,\allowbreak 0.01,\allowbreak -0.04,\allowbreak 0.06,\allowbreak 0.02,\allowbreak -0.01,\allowbreak 0.04,\allowbreak 0.03,\allowbreak -0.02,\allowbreak 0.05,\allowbreak -0.06,\allowbreak 0.02。信号月为月份 13 的月末,持有月为月份 14。12–1 的形成月份是 2 到 12(11 个月),月份 13(−6%-6\%)被跳过。

  • 复利:∏k=212(1+Rk)−1=17.716%\prod_{k=2}^{12}(1+R_k)-1=17.716\%;求和 17.0%17.0\%;差 0.716 个百分点。
  • 向前挪一个月(月份 1 到 11):15.474%15.474\%;向后挪一个月(月份 3 到 13,包含被跳过的月份):12.911%12.911\%;包含月份 13 的 12 个月(月份 2 到 13,即 12–0 窗口):10.653%10.653\%。

读法:同一个资产在三种错误窗口下的信号相差 2 至 7 个百分点;12–0 比 12–1 低了 7 个百分点,因为被跳过的月份是 −6%-6\%,若有大量资产像这样在最近一个月回吐,12–0 会把它们排得更靠后。

8.2 横截面:六个资产

6 个资产的 12–1 形成收益 (A,B,C,D,E,F)=(30%,12%,5%,−2%,−10%,−25%)(A,B,C,D,E,F)=(30\%,12\%,5\%,-2\%,-10\%,-25\%),下个月收益 (2%,3%,1%,0%,1%,−2%)(2\%,3\%,1\%,0\%,1\%,-2\%)。取最高 2 个与最低 2 个:做多 A、B,做空 F、E。多头腿平均 (2+3)/2=2.5%(2+3)/2=2.5\%,空头腿平均 (−2+1)/2=−0.5%(-2+1)/2=-0.5\%,多空 =2.5−(−0.5)=3.0%=2.5-(-0.5)=3.0\%。各腿资金 1,所以总暴露 2,净暴露 0。

8.3 时间序列:波动率缩放

三个资产信号 (+1,−1,+1)(+1,-1,+1),事前年化波动率 (10%,20%,40%)(10\%,20\%,40\%),目标 40%:单个仓位 (4,−2,1)(4,-2,1),除以 S=3S=3 得权重 (1.333,−0.667,0.333)(1.333,-0.667,0.333)。总暴露 2.3332.333,净暴露 1.01.0。下个月收益 (1%,−2%,3%)(1\%,-2\%,3\%),各资产贡献 (1.333%,1.333%,1.0%)(1.333\%,1.333\%,1.0\%),组合 3.667%3.667\%。读法:低波动资产权重大;组合净做多 1.0,说明它不是中性的。

8.4 拆股与分红

第3章构造例:拆股前 100,1 股拆为 2 股(美股称 2-for-1 split),拆股后 51,每新股分红 1。总回报 (2×51+2×1)/100−1=4%(2\times51+2\times1)/100-1=4\%;未调整价格变动 51/100−1=−49%51/100-1=-49\%。

8.5 期货展期

合约 A:100, 102100,\,102;展期时合约 B:106, 107106,\,107。拼接价格序列 100,102,106,107100,102,106,107,拼接收益 2.00%, 3.92%, 0.94%2.00\%,\,3.92\%,\,0.94\%;可得收益 2.00%2.00\%(持有 A)、0.94%0.94\%(持有 B);展期点的 3.92% 是假收益。

8.6 重叠持有与换手

两个资产,六个月的 vintage 权重:前两个月 (0.5,−0.5)(0.5,-0.5),第 3 个月起翻转为 (−0.5,0.5)(-0.5,0.5),其余月份保持。K=1K=1:相邻月换手 0,2,0,0,00,2,0,0,0,翻转当月交易 2.0。K=3K=3:组合 BB 是最新三个 vintage 的平均。表里从第 3 个月起才有三个 vintage:第 3 月 13(0.5+0.5−0.5)=0.167\tfrac13(0.5+0.5-0.5)=0.167,第 4 月 13(0.5−0.5−0.5)=−0.167\tfrac13(0.5-0.5-0.5)=-0.167,第 5 月 −0.5-0.5,第 6 月 −0.5-0.5(第一个资产的权重;第二个资产符号相反)。表内换手 0.667,0.667,00.667,0.667,0。若此前一直是 (0.5,−0.5)(0.5,-0.5),第 2 月的组合已是 (0.5,−0.5)(0.5,-0.5),则第 2 到第 3 月还有一次 0.6670.667,翻转的三个分摊月共交易 0.667×3=2.00.667\times3=2.0,与 K=1K=1 一次交易 2.0 相同:总量没变,但每个月的交易更小。若信号经常来回翻转,K=3K=3 会抵消一部分往返交易,总换手降低;真实行业数据上 K=1,3,6K=1,3,6 的换手均值是 0.904、0.518、0.364(11 节)。

8.7 熊市状态

市场月收益全为 +1%+1\%,只有第 3 个月是 −35%-35\%。信号月 s=24s=24(第 24 个月末):窗口月份 1 到 24 含第 3 个月,累计 1.0123×0.65−1=−18.3%<01.01^{23}\times0.65-1=-18.3\%<0,状态 1。s=26s=26:窗口月份 3 到 26 仍含第 3 个月,累计仍为 −18.3%-18.3\%,状态 1。s=27s=27:窗口月份 4 到 27 不再含第 3 个月,累计 1.0124−1=+26.97%1.01^{24}-1=+26.97\%,状态 0。窗口滑过崩溃月之后的第一个月,状态翻转。

9. Python 实现

窗口在 factor_signals.py 的 formation_return(第22章引入),组合与成本在新模块 momentum.py;截面分桶、IC、Newey–West 复用第23章的 factor_research.py。

函数 输入 输出与边界
formation_return 月频连续 PeriodIndex 的收益面板、window、skip 复利形成收益,信号月 ss 用月份 s−skip−window+1,…,s−skips-skip-window+1,\dots,s-skip;缺收益单元或窗口越界为 NaN;缺日历月行报错;−100%-100\% 以下报错
cross_sectional_momentum_weights 收益面板、窗口、跳月、桶数、持有月数 KK 每月 vintage(最高桶 +1/n+1/n、最低桶 −1/n-1/n)的最新 KK 个平均;历史不足的行为 NaN
blend_vintages 权重面板、KK 滚动平均,不足 KK 个 vintage 为 NaN
time_series_sign 超额收益面板、窗口、跳月 +1/−1/0+1/-1/0,窗口含缺失为 NaN
ex_ante_volatility 收益面板、窗口 36、最少 12 年化滚动标准差;教学替代,不是原文的日度指数加权估计
tsmom_weights 符号、波动率、目标波动率 sign⋅σ∗/σ/S\mathrm{sign}\cdot\sigma^*/\sigma/S;非正波动率、非正目标报错
strategy_returns 权重(信号月)、收益 信号月 ss 的权重乘 s+1s+1 月收益;非零权重遇缺失收益报错
net_of_costs 毛收益、权重、单位成本 毛收益减去 $c\times\sum
trailing_bear_state 市场总收益序列、窗口 过去 24 个月(含当月)累计为负记 1,窗口不足为 NaN

默认离线入口(从 I3 阅读包根目录运行):

.venv/bin/python run_notebook.py notebooks/ch25_momentum.ipynb

默认 RUN_FRENCH=False,展示窗口、复利、CS/TS权重及24×6、种子2的vintage恒等式(K=1/3/6)。五个市场单元均跳过,默认不导入ch25模块、不计算拆股/展期或历史网格。 runner另写 runs/execution_<stem>_<uuid>/ 的执行副本与状态。Notebook英语语言层及整包路径在BIL23装配;下列为历史完整CLI与测试参考,并非默认离线命令:

.venv/bin/python code/ch25_momentum.py
.venv/bin/python -m pytest tests/test_ch25_momentum.py tests/test_ch25_workflow.py -q

频率月;signal_time 是月末;成交在下一可交易时点;收益区间是 s+1s+1 月;French 行业与 FF3 经哈希核对读取,修订历史。单位成本 10 bp 是假设值,不是估计。

代码段一:12–1 窗口与三种错误窗口(8.1 节)。

import numpy as np
import pandas as pd

from quant_course.factor_signals import formation_return

r = [0.03, -0.02, 0.05, 0.01, -0.04, 0.06, 0.02, -0.01, 0.04, 0.03, -0.02, 0.05, -0.06, 0.02]
frame = pd.DataFrame({"a": r}, index=pd.period_range("2020-01", periods=14, freq="M"))
s = pd.Period("2021-01", "M")                                    # month 13: signal at the end of this month
f121 = formation_return(frame, window=11, skip=1).loc[s, "a"]    # months 2..12
f120 = formation_return(frame, window=12, skip=0).loc[s, "a"]    # months 2..13, nothing skipped (time-series style)
early = np.prod(1 + np.array(r[0:11])) - 1                      # months 1..11
late = np.prod(1 + np.array(r[2:13])) - 1                       # months 3..13
assert abs(f121 - 0.177159) < 1e-6 and abs(f120 - 0.106530) < 1e-6
assert abs(early - 0.154737) < 1e-6 and abs(late - 0.129112) < 1e-6
assert abs(sum(r[1:12]) - 0.17) < 1e-12                          # the sum, 0.716 percentage points lower
print(round(f121, 4), round(f120, 4), round(early, 4), round(late, 4))

代码段二:求和与复利给出相反的排序(6.2 节)。

import numpy as np
import pandas as pd

from quant_course.factor_research import rank_cross_section
from quant_course.factor_signals import formation_return

idx = pd.period_range("2020-01", periods=2, freq="M")
returns = pd.DataFrame({"X": [0.50, -0.50], "Y": [0.0, 0.0]}, index=idx)
compound = formation_return(returns, window=2, skip=0).iloc[-1]
total = returns.sum()
assert np.isclose(total["X"], 0.0) and np.isclose(total["Y"], 0.0)                              # sums tie
assert np.isclose(compound["X"], -0.25) and np.isclose(compound["Y"], 0.0)                      # compounding separates them
assert np.isclose(np.log1p(returns["X"]).sum(), np.log(0.75))                                   # log returns add up to the compound return
assert list(rank_cross_section(compound.to_numpy())) == [1.0, 2.0]                              # X is the loser
print(compound.round(4).to_dict())

代码段三:横截面六资产与时间序列权重(8.2、8.3 节)。

import numpy as np
import pandas as pd

from quant_course.factor_research import quantile_long_short_weights
from quant_course.momentum import tsmom_weights

idx = pd.period_range("2026-06", periods=1, freq="M")
names = list("ABCDEF")
formation = pd.DataFrame([[0.30, 0.12, 0.05, -0.02, -0.10, -0.25]], index=idx, columns=names)
w = quantile_long_short_weights(formation, 3).iloc[0]                  # terciles of 6 names: top 2 long, bottom 2 short
nxt = np.array([0.02, 0.03, 0.01, 0.00, 0.01, -0.02])
np.testing.assert_allclose(w.to_numpy(), [0.5, 0.5, 0, 0, -0.5, -0.5])
assert abs(w.to_numpy() @ nxt - 0.03) < 1e-12 and abs(w.sum()) < 1e-12 and abs(w.abs().sum() - 2.0) < 1e-12

sig = pd.DataFrame([[1.0, -1.0, 1.0]], index=idx, columns=list("xyz"))
vol = pd.DataFrame([[0.10, 0.20, 0.40]], index=idx, columns=list("xyz"))
tw = tsmom_weights(sig, vol, target_vol=0.40).iloc[0]
np.testing.assert_allclose(tw.to_numpy(), [4 / 3, -2 / 3, 1 / 3])
assert abs(tw.abs().sum() - 7 / 3) < 1e-12 and abs(tw.sum() - 1.0) < 1e-12                       # gross 2.33, net 1.0: not market neutral
assert abs(tw.to_numpy() @ np.array([0.01, -0.02, 0.03]) - 0.036667) < 1e-6
print(tw.round(3).to_dict())

代码段四:重叠持有的换手公式 turns=∥w(s)−w(s−K)∥1/K\mathrm{turn}_s=\|w^{(s)}-w^{(s-K)}\|_1/K(6.3 节)。

import numpy as np
import pandas as pd

from quant_course.momentum import blend_vintages

rng = np.random.default_rng(2)
idx = pd.period_range("2020-01", periods=24, freq="M")
vint = pd.DataFrame(rng.choice([-0.5, 0.0, 0.5], size=(24, 6)), index=idx)          # arbitrary vintage weights
for K in (1, 3, 6):
    blended = blend_vintages(vint, K)
    turnover = blended.diff().abs().sum(axis=1, min_count=1)
    formula = pd.Series([np.abs(vint.iloc[i] - vint.iloc[i - K]).sum() / K for i in range(len(vint))], index=idx)
    np.testing.assert_allclose(turnover.iloc[K:].to_numpy(), formula.iloc[K:].to_numpy(), atol=1e-12)
    assert turnover.iloc[:K].isna().all()                                      # no turnover before K vintages exist
print("turnover = ||w_s - w_(s-K)||_1 / K holds for K = 1, 3, 6")

代码段五:拆股、分红与期货展期(8.4、8.5 节)。

pre, ratio, dividend, post = 100.0, 2.0, 1.0, 51.0
assert abs((ratio * post + ratio * dividend) / pre - 1 - 0.04) < 1e-12 and abs(post / pre - 1 + 0.49) < 1e-12

a_prices, b_prices = [100.0, 102.0], [106.0, 107.0]
spliced = a_prices + b_prices
returns_spliced = [spliced[i + 1] / spliced[i] - 1 for i in range(3)]
tradable = [a_prices[1] / a_prices[0] - 1, b_prices[1] / b_prices[0] - 1]
assert abs(returns_spliced[1] - (106 / 102 - 1)) < 1e-15 and abs(returns_spliced[1] - 0.039216) < 1e-6      # fake return at the roll
assert abs(tradable[0] - 0.02) < 1e-15 and abs(tradable[1] - (107 / 106 - 1)) < 1e-15
print([round(x, 4) for x in returns_spliced], [round(x, 4) for x in tradable])

代码段六:熊市状态的窗口边界(8.7 节)与行业窗口的“没有未来信息”。

import numpy as np
import pandas as pd

from quant_course.momentum import cross_sectional_momentum_weights, trailing_bear_state

idx = pd.period_range("2000-01", periods=30, freq="M")
m = pd.Series(0.01, index=idx); m.iloc[2] = -0.35                           # month 3 is the crash month
state = trailing_bear_state(m, window=24)
assert np.isnan(state.iloc[22]) and state.iloc[23] == 1.0 and state.iloc[25] == 1.0 and state.iloc[26] == 0.0

rng = np.random.default_rng(4)
returns = pd.DataFrame(rng.normal(0.01, 0.05, (48, 10)), index=pd.period_range("2010-01", periods=48, freq="M"))
base = cross_sectional_momentum_weights(returns, window=11, skip=1, n_buckets=5, hold_months=3)
shocked = returns.copy(); shocked.iloc[30:] = 0.3
changed = cross_sectional_momentum_weights(shocked, window=11, skip=1, n_buckets=5, hold_months=3)
pd.testing.assert_frame_equal(base.iloc[:31], changed.iloc[:31])           # months >= 30 cannot change weights decided up to month 30
print(state.iloc[[22, 23, 25, 26]].tolist())

10. 图形解读

图 25-1:窗口时间轴。 横向是日历月 1 到 15,信号在月份 13 的月末。第一行:横截面 12–1 用月份 2 到 12(蓝色,11 个月),月份 13(斜线)被跳过,持有月份 14(橙色)。第二行:时间序列 12–0 用月份 2 到 13(紫色,12 个月),不跳月。第三行:窗口向前挪一个月(灰色,月份 1 到 11)是一种常见的错误。读法:三行的信号不是同一个数。

图25-1:12–1 与 12–0 的窗口端点

图 25-2:10 个行业的组合累计与最差月份。 左图纵轴是复利财富(对数刻度,起点为 1),样本 1927-07 起:横截面 12–1 的多空(K=1K=1 与 K=3K=3,毛收益,总暴露 2)、时间序列 12–0(超额收益,波动率缩放,总暴露约 2.5)、同样缩放的始终做多。读法:横截面多空长期上行但幅度小;时间序列趋势与始终做多同向,2000 年后始终做多更高。右图是横截面 12–1 的月度多空收益(% 每月),橙点是十个最差月份。样本是已修订的行业组合,不是成交。

图25-2:横截面、时间序列与始终做多的累计;横截面最差月份

图 25-3:J×KJ\times K 网格的 tt 值。 行是形成跨度 J∈{3,6,9,12}J\in\{3,6,9,12\}(使用月份 s−J+1,…,s−1s-J+1,\dots,s-1,J=12J=12 是标准 12–1),列是持有月数 K∈{1,3,6,12}K\in\{1,3,6,12\},格子里是五分位多空(每桶 2 个行业)的 Newey–West tt(滞后 K−1K-1)。16 个策略的名义 Bonferroni 双侧正态门槛是 z=2.955z=2.955。有效FWER控制要求边际p值已校准;这里只沿用NW/正态近似,滞后 K−1K-1 是冻结规格,不是所有依赖结构下的充分带宽。读法:越过门槛的只有 (12,1)(12,1) 的 3.64、(12,3)(12,3) 的 3.43 和 (9,6)(9,6) 的 3.18;(9,3)(9,3) 的 2.94 略低于门槛;形成跨度短且持有短的格子不显著。

图25-3:形成跨度与持有月数的 t 值网格

图 25-4:崩溃状态。 左图:把信号月按“过去 24 个月市场累计总收益是否为负”分成两类,横截面 12–1 的下月平均多空:非熊市 987 个月 +0.644%+0.644\%,熊市 191 个月 −0.179%-0.179\%(竖线是 ±2 倍标准误)。右图:每个点是一个月,横轴是该月市场总收益,纵轴是多空收益,橙色是熊市状态。读法:最大的损失出现在市场大涨的月份(右下角),多发生在熊市状态之后。

图25-4:熊市状态与多空收益

11. 历史市场案例

数据。 与第22–24章相同的冻结 French 10 行业价值加权月收益(处理后 CSV SHA-256 3b097d810c295d64eedfa1ece7067ff2a0707f5ca90e33935df9949251f70950,1926-07 至 2026-08 共 1,202 个月)以及 FF3 的 Mkt-RF 与 RF(处理后 SHA-256 c19770a19a0ac47ad1c29074f57234570aeaa9725212cb4fa8146a2846c52814)。行业组合是已修订的发布收益,10 个资产,不是个股,不是成交序列。本章没有新增下载。

横截面 12–1(五分位,每桶 2 个行业,等权,月度形成)。 信号月 1927-06 至 2026-07。12–1 形成收益在全部 1,191 个有形成收益的月份都没有并列,所以每桶恰为 2 个行业;一个月窗口的变体(1–0、1–1)各有 60 个月出现并列,并列者按平均名次处理,极端桶可能含 1 或 3 个行业,多空两腿各自的权重和仍为 1。

KK 毛均值(% 每月) 毛 tt(NW 滞后 K−1K-1) 净均值(每单位 10 bp) 净 tt 换手均值
1 0.519 3.64 0.427 2.99 0.904
3 0.474 3.43 0.423 3.06 0.518
6 0.347 2.71 0.308 2.40 0.364

均值口径。 表中K=1/3/6的毛收益分别有1190/1188/1185个月,净收益为1189/1187/1184个月。首次有效目标权重没有上一期可比权重,换手及净收益因此缺失;换手均值按权重差自身的有效行计算,还可能含没有下一月收益标签的末行。因此毛均值减净均值不必等于10bp乘此列换手均值。比较成本时须将毛收益和换手先对齐到同一有效月份;此表保留各历史序列自己的口径,不能从显示三位数反推新成本或修改历史结果。

月波动 4.9%(K=1K=1),年化毛夏普 0.37(K=1K=1)、净夏普 0.30。K=3K=3 的净夏普 0.31,高于 K=1K=1 与 K=6K=6。成本只是换手乘单位成本,没有计借券与融资,也没有容量;10 bp 是假设。

窗口变体(K=1K=1)。

窗口 月数 均值(% 每月) tt
12–1(月份 s−11..s−1s-11..s-1) 1190 0.519 3.64
12–0(月份 s−11..ss-11..s) 1190 0.621 4.41
1–0(月份 ss) 1201 0.740 5.61
1–1(月份 s−1s-1) 1200 −0.028-0.028 −0.21-0.21
6–1(月份 s−5..s−1s-5..s-1) 1196 0.209 1.50
3–1(月份 s−2..s−1s-2..s-1) 1199 −0.018-0.018 −0.13-0.13

这个表有六行,加上 J×KJ\times K 的 16 个策略,其中 12–1、6–1、3–1 在两张表里重复,本节共评价了 19 个不同的横截面变体(不含另列的 TS 评估;完整研究账本仍须计入后者),必须按第22章计入账本:19 次检验的 Bonferroni 双侧门槛是 z=3.01z=3.01。越过它的有 1–0(5.61)、12–0(4.41)、12–1(3.64)、(12,3)(12,3)(3.43)和 (9,6)(9,6)(3.18),其余没有。1–0(上个月收益)在行业层面是正的(没有短期反转),而只用 s−1s-1 月的 1–1 约为零。

J×KJ\times K 网格(NW tt;括号内为月均 % )。

J\KJ\backslash K 1 3 6 12
3 −0.13-0.13(−0.02-0.02) 0.60(0.06) 1.64(0.13) 2.84(0.17)
6 1.50(0.21) 1.70(0.22) 2.62(0.28) 2.23(0.19)
9 2.13(0.30) 2.94(0.39) 3.18(0.39) 1.62(0.15)
12 3.64(0.52) 3.43(0.47) 2.71(0.35) 0.78(0.08)

时间序列动量(超额收益;每个资产目标年化波动 40%;波动率为过去 36 个月标准差,至少 12 个月)。

组合 均值(% 每月) tt 月标准差(%) 年化夏普 总暴露均值 净暴露均值
TS 12–0(含最近月) 1.391 5.46 8.79 0.55 2.50 1.05
TS 12–1(跳月) 1.197 4.66 8.86 0.47 2.50 1.01
始终做多(同样缩放) 1.779 5.79 10.61 0.58 2.50 2.50

TS 12–0 比始终做多低 0.389%0.389\% 每月(差序列 t=−1.05t=-1.05)。横截面 12–1(K=1K=1)与时间序列 12–0 的月收益相关 0.42。资产被判为多头的平均比例 67.4%。

崩溃状态。 过去 24 个月市场总收益(Mkt-RF 加 RF)为负的信号月 191 个(占全部有状态月份的 16.2%):下月多空均值 −0.179%-0.179\%,标准差 7.05%;其余 987 个月均值 +0.644%+0.644\%,标准差 4.37%。十个最差月份:

信号月 → 收益月 多空(%) 熊市状态 收益月市场总收益(%)
1933-03 → 1933-04 −34.83-34.83 是 +38.91+38.91
1932-07 → 1932-08 −30.90-30.90 是 +37.15+37.15
2009-03 → 2009-04 −25.97-25.97 是 +10.18+10.18
2000-12 → 2001-01 −25.21-25.21 否 +3.79+3.79
1932-06 → 1932-07 −24.73-24.73 是 +33.63+33.63
1938-05 → 1938-06 −19.23-19.23 是 +23.89+23.89
2022-12 → 2023-01 −18.55-18.55 是 +6.96+6.96
1939-08 → 1939-09 −15.94-15.94 是 +16.90+16.90
2026-06 → 2026-07 −13.80-13.80 否 −0.28-0.28
2025-01 → 2025-02 −13.63-13.63 否 −2.11-2.11

7 个出现在熊市状态之后,8 个收益月的市场总收益为正。横截面 12–1(K=1K=1,毛)的复利累计最大回撤 64.3%。

能说什么,不能说什么。 可以说:10 个行业组合上,12–1 的横截面多空在 1927–2026 为正且显著(t=3.64t=3.64,毛),净成本后 t=2.99t=2.99;换手随 KK 增大下降;不同窗口与持有期的结果差别大,只有长形成跨度、短持有的格子越过 Bonferroni 门槛;最差的月份集中在熊市状态之后的反弹。不能说:(1)个股动量存在或不存在,样本是 10 个行业组合;(2)跳月无益,行业组合没有微观结构反转,不等于个股没有;(3)2000 年之后的弱化显著,第22、23章已经给出同一数据在 2000–2026 的 t=1.70t=1.70;(4)时间序列趋势优于始终做多,这个样本里没有;(5)净收益可得,10 bp 是假设,借券、融资与容量未计;(6)崩溃与市场反弹的关系已被证明,这里是描述,不是对 Daniel–Moskowitz 结果的复现。

12. 常见错误

错误 1:窗口差一个月。 8.1 节:同一资产的 12–1 信号是 17.72%,向前挪一个月 15.47%,向后挪一个月 12.91%,12–0 为 10.65%。修复:用 formation_return(window=11, skip=1) 并让第23章代码段五的“改未来不改过去”测试通过。

错误 2:求和代替复利。 6.2 节:+50%,−50%+50\%,-50\% 求和 0,复利 −25%-25\%。修复:简单收益用复利,对数收益相加后取指数。

错误 3:横截面与时间序列的跳月不一致。 把 MOP 的 12–0 与 JT 的 12–1 混在一个对比里,会把窗口差异当作策略差异:行业数据上 12–0 的均值 0.621% 与 12–1 的 0.519% 相差 0.10 个百分点。修复:并列报告,明确写出每一列的窗口。

错误 4:未调整价格的拆股。 8.4 节:拆股使价格下降 49%,总回报 +4%。下面的例子里一个拆股后的“假输家”被排在最后,一个真正的输家被排在前面。

import numpy as np
import pandas as pd

from quant_course.factor_research import rank_cross_section

idx = pd.period_range("2020-01", periods=3, freq="M")
prices = pd.DataFrame({"split_stock": [100.0, 51.0, 52.0], "true_loser": [100.0, 90.0, 85.0]}, index=idx)   # 2-for-1 split in month 2 (+$1 dividend per new share)
unadjusted = prices.iloc[-1] / prices.iloc[0] - 1
adjusted_split = (2 * 51.0 + 2 * 1.0) / 100.0 * (52.0 / 51.0) - 1                    # total-return chain: split month, then 52/51
adjusted = pd.Series({"split_stock": adjusted_split, "true_loser": 85.0 / 100.0 - 1})
rank_u, rank_a = rank_cross_section(unadjusted.to_numpy()), rank_cross_section(adjusted.to_numpy())
assert rank_u[0] == 1.0 and rank_a[0] == 2.0                                          # unadjusted: split stock is the worst; adjusted: it is the best
print(unadjusted.round(3).to_dict(), adjusted.round(3).to_dict())

错误 5:期货价格直接拼接。 6.8 节:3.92% 的假收益。时间序列信号会把展期跳跃当成趋势。

import numpy as np

a_prices, b_prices = [100.0, 102.0, 104.0], [108.0, 109.0, 111.0]          # contract B trades at a premium: contango
spliced = np.array([100.0, 102.0, 108.0, 109.0, 111.0])                     # roll between the second and third prices
naive_return = spliced[-1] / spliced[0] - 1                                  # "12-month" return from the spliced series
true_a = a_prices[1] / a_prices[0] - 1
true_b = b_prices[2] / b_prices[0] - 1
true_total = (1 + true_a) * (1 + true_b) - 1                                  # hold A to the roll, then hold B
assert naive_return > true_total + 0.04                                       # the splice overstates the move by the roll gap
assert np.sign(naive_return) == np.sign(true_total) == 1
print(round(naive_return, 4), round(true_total, 4))

错误 6:现存股票池回测。 用今天仍上市的名单回到历史,等于用“活到现在”这一未来信息筛选股票:在下面固定赢家腿的例子里,剔除退市输家使输家腿未来收益被高估,WML被低估;一般动态股票池还会改变赢家、排名与权重,偏差方向不固定。数值例:10 只形成期的输家,9 只后来存活并平均上涨 +20%+20\%,1 只退市(−100%-100\%);真实输家腿平均 (9×20−100)/10=+8%(9\times20-100)/10=+8\%,只含存活者的回测得 +20%+20\%。修复:股票池按 signal_time 当时的成员;后续标签再计入实现的退市收益,不能用今天的名单筛选历史或把未来退市收益当作信号输入。本章没有个股数据,Project 4 准备层把它列为数据缺口。

错误 7:重叠持有收益用朴素 tt。 KK 个月持有的组合月收益序列相关,本章采用 Newey–West(冻结滞后 K−1K-1,须另核是否覆盖实际依赖);第23章已显示滞后 K−1K-1 在有限样本里仍偏乐观,这里的 tt 是 NW 值,只是量级参考。

错误 8:忽略换手与成本。 横截面 12–1 的月换手 0.904(毛暴露 2):10 bp 的单位成本占去 0.092 个百分点,约 18% 的毛均值;K=1K=1 的净 tt 从 3.64 降到 2.99。修复:成本在毛收益旁边报告,并说明借券与融资未计。

错误 9:窗口搜索不入账本。 本章评价了窗口变体与 J×KJ\times K 网格共 19 个不同的横截面策略,门槛应是 z≈3.01z\approx3.01 而不是 1.96。修复:按第22章记账,并只在预先写好的主规格上下结论。

13. 量化研究应用

Project 4 的动量字段与真实研究所需清单。 当前机器信号调用 formation_return(window=11, skip=1),价格契约列名为 total_return,定义为构造的小数月度总回报,signal_time 为月末;构造价格层没有记录真实 available_at。下表是未来真实研究需要的描述性字段清单(例如 total_return_m 不是当前机器列名),不能将其中供应商或可得时间字段称为本层已经取得:

字段 来源 时间字段 单位 缺失规则
total_return_m 总回报价格或供应商总收益 月末(event_time),available_at 小数 缺收益单元使窗口为 NaN;缺日历月行应先拒绝,不填补
mom_12_1 计算 signal_time 小数 历史不足 12 个月为 NaN
rank_pct 同日百分位 signal_time 0–1 同日少于 2 个有效资产为 NaN
universe_member 历史成分 signal_time 时的版本,含退市 布尔 无历史成分则不得声称回测
label_1m 下月收益(含退市收益) 标签区间起止 小数 退市月使用退市收益,不用 NaN 丢弃

研究流程。 先写冻结计划(第22章):假设“同日 12–1 收益更高的资产下月收益更高”,信号窗口 12–1,K=1K=1,主指标逐期 Rank IC 的 NW tt,判据与成本假设;再评价主规格;窗口变体与 KK 网格只作稳健性,进账本,不改变主结论。必须同时报告:毛与净收益、换手、崩溃状态下的均值与最差月份、窗口变体、与基准(横截面对零,时间序列对始终做多)的差。

本章完成的内容与没有完成的内容。 完成:窗口与组合的核心函数、10 个行业的完整报告。没有完成:个股层面的动量(缺历史成分、退市收益、总回报价格),Project 4 的成本后策略(第 35–36 章),动态动量。第28章把动量与价值、质量合成。

13.1 收益口径和 Project 4 接口逐项对照

要计算的对象 必须取得的输入 不能用什么替代
股票月度总回报 持股数变化、拆股、现金分配与退市处理,或明确这些调整的总回报序列 未调整收盘价差;已含分红的总回报再加一次分红
股票价格收益 拆股调整价格,另标不含现金分配 将它称作总回报动量
期货收益 同一合约持有期间的P&L、展期规则、名义敞口/保证金口径及抵押品利息处理 跨合约的拼接价格差;它不是展期成交利润
当前P4构造总回报 生成器定义的小数月简单总回报,连续月索引,按当时成员取样 真实交易所价格或实盘成交证据

股票例里若其他形成月的财富乘积为 GG,正确信号是 G(1+Rtotal)−1G(1+R_{\rm total})-1,未调整信号是 G(1+Rraw)−1G(1+R_{\rm raw})-1,差为 G(Rtotal−Rraw)G(R_{\rm total}-R_{\rm raw})。第6.7节拆股例的当月差是53个百分点;若 G=1.10G=1.10,形成信号差是58.3个百分点。期货的价格相对变动还须注明这是单位名义敞口的简单教学代理,保证金账户回报与含抵押品利息的总回报另有分母。

Project 4 mom_12_1 实际调用 formation_return(..., 11, 1),收益输入是契约中的构造月度总回报,信号月 ss 之后以 s+1s+1 收益作为标签。本层没有真实调整价格来源、日频下一可交易时刻或成交核验。原始形成信号进入第24章13.1节的统一预处理与合成流程;它不是直接持仓。标签对齐、Rank IC与时间推断用第23章,重叠持有的NW滞后是既有规格,不能将 K−1K-1 视为覆盖所有序列依赖的普遍充分条件。

14. 本章小结

动量由三件事决定:窗口端点(12–1 是月份 s−11s-11 到 s−1s-1,MOP 的 12–0 含最近月,差一个月不是同一个信号)、复利(求和会改变排序)、组合构造(横截面多空净暴露为零,时间序列趋势可以净多或净空,必须与始终做多对比)。重叠持有每月替换 1/K1/K 的持仓批次份额;在每期重置平均目标且忽略漂移的规则下,目标权重换手为 ∥w(s)−w(s−K)∥1/K\|w^{(s)}-w^{(s-K)}\|_1/K。数据口径决定结果是否可信:拆股与分红需要总回报,期货需要单一合约的收益而非拼接价格,股票池需要 signal_time 当时的成员。10 个行业组合上,12–1 为正且在成本后仍为正,但 19 个不同的横截面变体里只有少数越过多重检验门槛;时间序列趋势没有超过始终做多;最差月份集中在熊市状态之后的反弹。适用边界:行业组合不是个股;成本是假设;崩溃的描述不是复现;本章没有个股数据。

15. 练习

25-E01(手算) 某资产 14 个月收益(月份 1 到 14):0.04,−0.03,0.02,0.05,−0.01,0.03,0.02,−0.02,0.01,0.06,−0.04,0.02,0.03,−0.050.04,\allowbreak -0.03,\allowbreak 0.02,\allowbreak 0.05,\allowbreak -0.01,\allowbreak 0.03,\allowbreak 0.02,\allowbreak -0.02,\allowbreak 0.01,\allowbreak 0.06,\allowbreak -0.04,\allowbreak 0.02,\allowbreak 0.03,\allowbreak -0.05。信号在月份 13 的月末。计算 12–1、12–0 与向前、向后各挪一个月的窗口收益,以及 12–1 的求和值。

25-E02(手算) 8 个资产的 12–1 形成收益 (40%,25%,18%,10%,10%,−5%,−12%,−30%)(40\%,25\%,18\%,10\%,10\%,-5\%,-12\%,-30\%),下月收益 (3%,1%,4%,0,2%,1%,−2%,−3%)(3\%,1\%,4\%,0,2\%,1\%,-2\%,-3\%)。取 q=4q=4 的最高桶与最低桶(并列处理按平均名次),求 q=4q=4 的桶号、多空权重与下月多空收益。

25-E03(手算) 四个资产符号 (+1,−1,−1,+1)(+1,\allowbreak -1,\allowbreak -1,\allowbreak +1),事前年化波动率 (8%,16%,32%,20%)(8\%,\allowbreak 16\%,\allowbreak 32\%,\allowbreak 20\%),目标 40%,下月收益 (1.5%,−3%,4%,−1%)(1.5\%,\allowbreak -3\%,\allowbreak 4\%,\allowbreak -1\%)。求权重、总暴露、净暴露、组合收益与各资产贡献。

25-E04(手算) 两个资产,七个月 vintage:前三个月 (0.5,−0.5)(0.5,-0.5),后四个月 (−0.5,0.5)(-0.5,0.5)。求 K=1K=1 与 K=3K=3 的目标权重换手,并验证 turns=∥w(s)−w(s−K)∥1/K\mathrm{turn}_s=\|w^{(s)}-w^{(s-K)}\|_1/K。

25-E05(手算) 某股票价格 90,1 股拆为 3 股(3-for-1 split),拆股后价格 31,每新股现金分红 0.5。求总回报与未调整价格变动;若它在 12–1 形成窗口里,信号被低估多少个百分点?

25-E06(手算) 合约 A 的价格 50、52;展期时合约 B 的价格 55、56.1。求拼接价格的“收益”序列、可得收益序列与展期点的假收益。

25-E07(手算) 市场月收益全为 +1%+1\%,只有第 3 个月为 −35%-35\%。24 个月窗口的熊市状态在信号月 s=23,24,26,27s=23,24,26,27 分别是多少?说明状态翻转的原因。

25-E08(概念与手算) 资产 X 的两个月收益 +50%,−50%+50\%,-50\%,Y 的为 0,00,0。分别用求和与复利排序;再给出三个月的例子,求和排序与复利排序相反。

25-E09(解释) 表 11 里 TS 12–0 的月均 1.391%,始终做多 1.779%。解释为什么不能把 1.391% 当作趋势的 alpha,应怎样检验趋势是否增加了价值。

25-N01(日历与数据诊断) 从第3.1节实际月份表计算12–1、12–0与求和,标出形成/跳月/持有时点。若有人把2026-01的收益乘入信号,指出泄漏。再令第6.7节拆股例中其他形成月财富乘积 G=1.10G=1.10,求使用未调整价格造成的形成信号差,并说明期货换月时为什么不能赚到拼接跳跃。

16. 编程练习

25-P01 写测试:给定 14 个月收益,断言 formation_return(window=11, skip=1) 在月份 13 等于 8.1 节的值,并断言改变月份 13 及之后的收益不改变该行。验收:两条断言通过。

25-P02 构造 24 个月、10 个资产的随机权重 vintage,计算 K=1,3,6K=1,3,6 的组合换手,验证公式 ∥w(s)−w(s−K)∥1/K\|w^{(s)}-w^{(s-K)}\|_1/K。验收:三个 KK 值误差小于 10−1210^{-12}。

25-P03 在构造的带正漂移的随机游走面板(10 个资产,480 个月,月漂移 0.5%,月波动 4%)上计算时间序列 12–0 与同样缩放的始终做多,输出均值、tt 与差序列的 tt;解释结果。验收:始终做多的均值高于时间序列组合,差序列 t<−2t<-2。

17. 挑战题

25-C01(证明) 证明重叠持有组合 Bs=1K∑j=0K−1w(s−j)B_s=\frac1K\sum_{j=0}^{K-1}w^{(s-j)} 的相邻换手等于 ∥w(s)−w(s−K)∥1/K\|w^{(s)}-w^{(s-K)}\|_1/K;若每个 vintage 的总暴露为 2,给出换手上界并说明何时取到;说明为什么 KK 增大不必然使换手按 1/K1/K 下降。额外先修:绝对值的三角不等式。评分:证明 40,上界 30,讨论 30。

25-C02(开放) 为 Project 4 的动量信号写完整的冻结计划与数据缺口表:窗口、总回报口径、历史成分与退市收益、成本与借券假设、崩溃状态的报告方式、多重检验计数。指出哪些字段在本课程现有数据里没有。评分:计划完整性 25,时间字段正确性 25,数据缺口 25,崩溃与成本的处理 25。

18. 完整答案与参考实现

25-E01 12–1:月份 2 到 12 的复利 ∏k=212(1+Rk)−1\prod_{k=2}^{12}(1+R_k)-1:逐月乘积 0.97×1.02×1.05×0.99×1.03×1.02×0.98×1.01×1.06×0.96×1.02=1.11010.97\times1.02\times1.05\times0.99\times1.03\times1.02\times0.98\times1.01\times1.06\times0.96\times1.02=1.1101,即 +11.01%+11.01\%(舍入显示 11.009%11.009\%);求和 11.0%11.0\%。12–0(月份 2 到 13,加月份 13 的 +3%+3\%):1.1101×1.03=1.14341.1101\times1.03=1.1434,+14.34%+14.34\%。向前挪一个月(月份 1 到 11):13.19%13.19\%;向后挪一个月(月份 3 到 13):17.88%17.88\%。四个窗口相差 2 到 7 个百分点。

25-E02 按资产顺序,形成收益 (40,25,18,10,10,−5,−12,−30)%(40,25,18,10,10,-5,-12,-30)\% 的升序名次:−30→1-30\to1,−12→2-12\to2,−5→3-5\to3,两个 10 并列占 4、5 各 4.5,18→618\to6,25→725\to7,40→840\to8,所以名次 (8,7,6,4.5,4.5,3,2,1)(8,7,6,4.5,4.5,3,2,1)。q=4q=4,n=8n=8:桶号 ⌊4(2r−1)/16⌋+1\lfloor4(2r-1)/16\rfloor+1:r=8→4r=8\to4,7→47\to4,6→36\to3,4.5→34.5\to3,3→23\to2,2→12\to1,1→11\to1,即 (4,4,3,3,3,2,1,1)(4,4,3,3,3,2,1,1)。最高桶是前两个资产(形成收益 40%、25%),最低桶是最后两个(−12%-12\%、−30%-30\%)。权重各腿 ±0.5\pm0.5。多空 =12(3%+1%)−12(−2%−3%)=2%+2.5%=4.5%=\tfrac12(3\%+1\%)-\tfrac12(-2\%-3\%)=2\%+2.5\%=4.5\%。

25-E03 单个仓位 sign×0.40/σ\mathrm{sign}\times0.40/\sigma:(+5,−2.5,−1.25,+2)(+5,\allowbreak -2.5,\allowbreak -1.25,\allowbreak +2);除以 4:权重 (1.25,−0.625,−0.3125,0.5)(1.25,\allowbreak -0.625,\allowbreak -0.3125,\allowbreak 0.5)。总暴露 2.68752.6875,净暴露 0.81250.8125。贡献:1.25×1.5%=1.875%1.25\times1.5\%=1.875\%,−0.625×(−3%)=1.875%-0.625\times(-3\%)=1.875\%,−0.3125×4%=−1.25%-0.3125\times4\%=-1.25\%,0.5×(−1%)=−0.5%0.5\times(-1\%)=-0.5\%;组合 2.0%2.0\%。

25-E04 K=1K=1:相邻换手 0,0,2,0,0,00,0,2,0,0,0,第 4 个月翻转,交易 2.0。K=3K=3:B3=(0.5,−0.5)B_3=(0.5,-0.5);B4=13(0.5+0.5−0.5)=(0.1667,−0.1667)B_4=\tfrac13(0.5+0.5-0.5)=(0.1667,-0.1667);B5=13(0.5−0.5−0.5)=(−0.1667,0.1667)B_5=\tfrac13(0.5-0.5-0.5)=(-0.1667,0.1667);B6=(−0.5,0.5)B_6=(-0.5,0.5);B7=(−0.5,0.5)B_7=(-0.5,0.5)。换手 0.667,0.667,0.667,00.667,0.667,0.667,0。验证:第 4 个月 ∥w(4)−w(1)∥1/3=∥(−0.5,0.5)−(0.5,−0.5)∥1/3=2/3\|w^{(4)}-w^{(1)}\|_1/3=\|(-0.5,0.5)-(0.5,-0.5)\|_1/3=2/3;第 5、6 个月同理;第 7 个月 ∥w(7)−w(4)∥=0\|w^{(7)}-w^{(4)}\|=0。总量 2.0,但每月只交易 0.667。

25-E05 总回报 (3×31+3×0.5)/90−1=(93+1.5)/90−1=+5.0%(3\times31+3\times0.5)/90-1=(93+1.5)/90-1=+5.0\%;未调整价格变动 31/90−1=−65.6%31/90-1=-65.6\%。拆股单月差为 5.0−(31/90−1)×100=70.55555565.0-(31/90-1)\times100=70.5555556 个百分点(显示70.6)。若其余形成月份的财富乘积为 GG,完整形成信号差为 G×70.5555556G\times70.5555556 个百分点;题中未给 GG,不能唯一确定。只有 G=1G=1 时才等于单月差。

25-E06 拼接价格 50,52,55,56.150,52,55,56.1:收益 4.00%,  5.77%,  2.00%4.00\%,\;5.77\%,\;2.00\%。可得收益:持有 A 52/50−1=4.00%52/50-1=4.00\%;展期无收益;持有 B 56.1/55−1=2.00%56.1/55-1=2.00\%。展期点假收益 55/52−1=5.77%55/52-1=5.77\%。拼接的两段总价格变化 56.1/50−1=12.2%56.1/50-1=12.2\%,可得总收益 1.04×1.02−1=6.08%1.04\times1.02-1=6.08\%。

25-E07 月份按 1 到 30 编号,窗口月份 s−23,…,ss-23,\dots,s,需要 24 个月历史。s=23s=23:历史不足,状态为 NaN。s=24s=24:窗口 1 到 24 含第 3 个月,累计 1.0123×0.65−1=−18.3%<01.01^{23}\times0.65-1=-18.3\%<0,状态 1。s=26s=26:窗口 3 到 26 仍含第 3 个月,状态 1。s=27s=27:窗口 4 到 27 不含第 3 个月,累计 1.0124−1=+26.97%1.01^{24}-1=+26.97\%,状态 0。翻转的原因:窗口滑过崩溃月之后,累计收益不再包含它。

25-E08 X:求和 0,复利 −25%-25\%;Y:求和 0,复利 0。求和并列,复利 Y 高于 X。三个月:X 的月收益 +30%,+30%,−40%+30\%,+30\%,-40\%,求和 +20%+20\%,复利 1.3×1.3×0.6−1=+1.4%1.3\times1.3\times0.6-1=+1.4\%;Y 的月收益 +10%,+5%,+4%+10\%,+5\%,+4\%,求和 +19%+19\%,复利 1.1×1.05×1.04−1=+20.1%1.1\times1.05\times1.04-1=+20.1\%。求和排序 X(20%)>Y(19%),复利排序 Y(20.1%)>X(1.4%):相反。

25-E09 趋势组合平均净暴露 1.05、67% 的资产被判为多头,所以 1.391% 里含有(缩放后的)股票风险溢价:始终做多(同样缩放)就有 1.779%。检验趋势的价值要用差序列(TS − 始终做多)或把 TS 对始终做多回归取截距:差序列均值 −0.389%-0.389\%(t=−1.05t=-1.05),截距的符号与显著性视回归系数而定。本章只报告差序列,结论是这个样本里没有观察到统计明确的正增量,不能由不显著证明增量为零。

25-P01 参考实现

import numpy as np
import pandas as pd

from quant_course.factor_signals import formation_return

r = [0.03, -0.02, 0.05, 0.01, -0.04, 0.06, 0.02, -0.01, 0.04, 0.03, -0.02, 0.05, -0.06, 0.02]
idx = pd.period_range("2020-01", periods=14, freq="M")
frame = pd.DataFrame({"a": r}, index=idx)
s = idx[12]
base = formation_return(frame, window=11, skip=1)
assert abs(base.loc[s, "a"] - (np.prod(1 + np.array(r[1:12])) - 1)) < 1e-12
shocked = frame.copy(); shocked.iloc[12:] = 0.9                                  # month 13 and later change
pd.testing.assert_frame_equal(base.iloc[:13], formation_return(shocked, window=11, skip=1).iloc[:13])

25-P02 参考实现:见第 9 节代码段四,保持原有K=1/3/6循环,将输入替换为 vint = pd.DataFrame(rng.choice([-0.5, 0.0, 0.5], size=(24, 10)), index=idx, columns=[f"A{i}" for i in range(10)]),其余差分、公式与断言不变;这给出题干要求的10列。

25-P03 参考实现

import numpy as np
import pandas as pd

from quant_course.momentum import ex_ante_volatility, strategy_returns, time_series_sign, tsmom_weights

rng = np.random.default_rng(3)
idx = pd.period_range("1980-01", periods=480, freq="M")
returns = pd.DataFrame(rng.normal(0.005, 0.04, (480, 10)), index=idx, columns=[f"A{i}" for i in range(10)])
vol = ex_ante_volatility(returns)
sig = time_series_sign(returns, window=12, skip=0)
ts = strategy_returns(tsmom_weights(sig, vol, target_vol=0.40), returns).dropna()
always = strategy_returns(tsmom_weights(pd.DataFrame(1.0, index=idx, columns=returns.columns).where(vol.notna()), vol, target_vol=0.40), returns).dropna()
diff = (ts - always).dropna()
t = diff.mean() / (diff.std(ddof=1) / np.sqrt(len(diff)))
assert always.mean() > ts.mean() and t < -2                     # independent returns: no trend to exploit; the 12-month sign filter shorts after a negative cumulative return and gives up the drift
print(round(100 * ts.mean(), 3), round(100 * always.mean(), 3), round(t, 2))

输出约为 0.500、1.565、−5.14-5.14:收益相互独立、只有正漂移时,符号过滤在过去12个月累计收益为负时做空,放弃了漂移,差序列显著为负(5 个种子里 tt 都小于 −2.8-2.8)。这是给定 iid 正漂移构造的符号过滤例,并不证明正收益自相关是趋势增量价值的一般必要条件;非线性可预测性、状态依赖和风险缩放也可能影响比较。行业样本差序列 t=−1.05t=-1.05 只表示未见统计明确的均值优势,不能识别原因或证明没有增量。

25-C01 Bs−Bs−1=1K(∑j=0K−1w(s−j)−∑j=1Kw(s−j))=1K(w(s)−w(s−K))B_s-B_{s-1}=\tfrac1K\bigl(\sum_{j=0}^{K-1}w^{(s-j)}-\sum_{j=1}^{K}w^{(s-j)}\bigr)=\tfrac1K(w^{(s)}-w^{(s-K)})(两个和共有 j=1..K−1j=1..K-1 的项)。取 ℓ1\ell_1 范数得 turns=∥w(s)−w(s−K)∥1/K\mathrm{turn}_s=\|w^{(s)}-w^{(s-K)}\|_1/K。每个 vintage 总暴露 2,三角不等式 ∥w(s)−w(s−K)∥1≤∥w(s)∥1+∥w(s−K)∥1=4\|w^{(s)}-w^{(s-K)}\|_1\le\|w^{(s)}\|_1+\|w^{(s-K)}\|_1=4,所以 turns≤4/K\mathrm{turn}_s\le4/K;取等号当且仅当对每个资产 ii 都有 wi(s)wi(s−K)≤0w^{(s)}_iw^{(s-K)}_i\le0(该资产在两个 vintage 中符号相反,或至少一个为零)。KK 增大时分母变大,但分子 ∥w(s)−w(s−K)∥1\|w^{(s)}-w^{(s-K)}\|_1 也会变化,并无随 KK 单调增大的普遍定理(周期性信号就可能先增后减),所以换手不按 1/K1/K 严格下降:行业数据上 K=3K=3 的分子均值约 1.55,K=1K=1 的约 0.90。评分按原稿三项。

25-C02(评分依据与参考) 计划要点:假设与符号(赢家下月收益更高,符号 +);信号 12–1 总回报;股票池按 signal_time 的历史成分,含退市收益;窗口 11 个月跳月 1 个月;组合最高与最低十分位等权,K=1K=1 主规格;成本假设按单位换手与借券费分别给出;主指标逐期 Rank IC 的 NW tt;崩溃状态:过去 24 个月市场总收益为负时的条件均值与最差月份;试验预算与账本;留出样本。数据缺口:个股总回报与调整价格、历史成分与退市收益、借券费率与可借股数,本课程现有数据均没有。评分按原稿四项。

25-N01完整答案。 2025-01至11共11项,累计财富1.1547369892,12–1为15.4737%;求和15%;含12月的12–0为21.2474%。12月是跳月;1月2026是下月持有标签,信号形成时未知,加入是前视。拆股例正确单月4%、未调整单月-49%,差53个百分点,乘 G=1.10G=1.10 后形成信号差58.3个百分点。展期同刻卖A买B没有从A的102到B的106的价格上涨路径;两个不同索取权的价格差不能计为持有收益,须分别计算各合约持有P&L。

19. 延伸阅读

  • Must Know:Jegadeesh 与 Titman(1993)§I 与 §II 开头,读 J/K 策略、重叠持有与对 32 次检验的 Bonferroni 说明。
  • Must Know:Asness、Moskowitz 与 Pedersen(2013)印刷页 936–937,读 MOM2–12 的定义与跳月理由。
  • Should Know:Moskowitz、Ooi 与 Pedersen(2012)§2.4 与 §4.1,读波动率缩放与 TSMOM 的式 (5)。
  • Should Know:Daniel 与 Moskowitz(2014 工作稿)摘要与表 3 说明,读崩溃状态的定义;期刊终稿未读。
  • Advanced:Moskowitz–Grinblatt(1999)行业动量原文;本章未读。
  • Optional:French 数据库 Mom 因子说明页,核对 prior (2-12) 的定义。

20. 参考资料

引用键与读取范围见 source_notes.md。

  • CH25_JT1993:Jegadeesh, N., Titman, S. (1993). Journal of Finance 48(1),印刷页 68–69。
  • CH25_AMP2013:Asness, C. S., Moskowitz, T. J., Pedersen, L. H. (2013). Journal of Finance 68(3),印刷页 936–937。
  • CH25_MOP2012:Moskowitz, T. J., Ooi, Y. H., Pedersen, L. H. (2012). Journal of Financial Economics 104(2),§2.4、§4.1。
  • CH25_DM2014:Daniel, K., Moskowitz, T. J. (2014). NBER Working Paper 20439,摘要、印刷页 8、表 3 说明;期刊终稿未读。
  • CH25_FRENCH_MOM_PAGE:Ken French 数据库 Mom 因子说明页。
  • FRENCH:French 行业与 FF3 冻结快照(data/manifests/)。

搜索教材

输入关键词开始搜索。