4、动量与反转因子:行业动量因子构建、动量衰减周期、反转效应识别
聊到行业轮动,动量与反转是绕不开的两个核心概念。我个人习惯把它们比作市场的「惯性」和「弹簧」——涨多了会继续涨一阵,但涨太猛了又会弹回来。今天我们就来拆解这两个因子,看看怎么用它们捕捉行业轮动的节奏。
4.1 行业动量因子构建
动量因子,说白了就是「强者恒强」。我刚开始做量化那会儿,以为动量就是简单算个过去N天的收益率,然后排序买入。后来发现,事情远没那么简单。
一个靠谱的行业动量因子,至少要考虑三个维度:
- 收益率动量:过去一段时间(比如20个交易日)的累计涨幅
- 超额动量:行业相对于大盘的超额收益
- 波动率调整动量:用波动率对收益率做标准化处理
为什么要做波动率调整?举个例子,两个行业过去20天都涨了10%,但A行业波动率是20%,B行业是5%。你想想看,A行业那10%的涨幅可能只是运气,B行业才是真正的趋势。我在项目中遇到过这种情况,直接用原始动量选出来的行业,回撤大得吓人。
下面是我常用的一个动量因子构建代码:
def build_momentum_factor(df, window=20):
"""
构建行业动量因子
df: 包含行业指数日度收益率的数据框
window: 动量计算窗口
"""
# 计算累计收益率
df['cum_ret'] = (1 + df['daily_ret']).rolling(window).prod() - 1
# 计算波动率(年化)
df['vol'] = df['daily_ret'].rolling(window).std() * np.sqrt(252)
# 波动率调整动量
df['momentum'] = df['cum_ret'] / df['vol']
# 还可以加入衰减权重(近期权重更大)
weights = np.exp(np.linspace(-1, 0, window))
weights = weights / weights.sum()
df['weighted_momentum'] = df['daily_ret'].rolling(window).apply(
lambda x: np.dot(x, weights)
)
return df
个人经验:我建议动量窗口不要只用单一周期。20天、60天、120天的动量信号做个综合打分,效果往往比单周期好。但注意,周期越长,信号越滞后,这是个trade-off。
4.2 动量衰减周期
动量不是永恒的。它像一杯热水,放久了总会凉。这个「凉」的过程,就是动量衰减。
为什么会衰减?原因有三:
- 拥挤交易:追涨的人太多,把价格推到了不合理的位置
- 均值回归:基本面不支持持续上涨,价格终将回归
- 资金轮动:获利盘了结,转向其他低估板块
我做过一个统计,A股行业动量的平均有效周期大约是3-6个月。超过这个时间,动量效应就会明显减弱,甚至反转。嗯,这里要注意,不同市场、不同行业,衰减速度是不一样的。
比如科技行业,动量衰减就很快,可能1-2个月就失效了。为什么?因为科技股波动大,资金进出频繁,趋势很难持续。而消费行业,动量可以维持半年以上,因为业绩稳定,资金抱团效应强。
| 行业类型 | 动量有效周期 | 衰减速度 | 典型代表 |
|---|---|---|---|
| 科技/成长 | 1-2个月 | 快 | 半导体、计算机 |
| 消费/医药 | 3-6个月 | 中等 | 白酒、医药 |
| 周期/金融 | 6-12个月 | 慢 | 煤炭、银行 |
避坑指南:我曾经在2018年用6个月动量做周期行业,结果被狠狠打脸。后来才发现,周期行业的动量受宏观经济影响极大,单纯用历史价格动量根本不够。一定要结合库存周期、PPI等宏观指标一起看。
4.3 反转效应识别
反转效应,就是「跌多了会涨,涨多了会跌」。听起来简单,但识别反转的时机才是关键。
我常用的反转识别方法有三种:
- 短期反转:过去5-10天跌幅过大,出现超卖信号
- 中期反转:过去1-3个月趋势衰竭,成交量萎缩
- 极端反转:行业指数偏离均线超过2个标准差
这里有个技巧——动量与反转的切换点。我个人习惯用「动量衰减率」来判断:当动量因子的值从高点回落超过30%,同时成交量开始放大,这就是反转可能发生的信号。
来看一个反转识别的代码示例:
def identify_reversal(df, momentum_col='momentum', threshold=0.3):
"""
识别反转信号
threshold: 动量衰减阈值
"""
# 计算动量衰减率
df['momentum_decay'] = df[momentum_col].pct_change(5)
# 判断反转条件
df['reversal_signal'] = 0
# 条件1:动量从高点回落超过30%
cond1 = df['momentum_decay'] < -threshold
# 条件2:成交量放大(相比20日均值)
cond2 = df['volume'] > df['volume'].rolling(20).mean() * 1.2
# 条件3:价格偏离20日均线超过5%
cond3 = abs(df['close'] / df['close'].rolling(20).mean() - 1) > 0.05
df.loc[cond1 & cond2 & cond3, 'reversal_signal'] = 1
return df
核心要点:反转信号不能单独使用。我建议把反转信号和动量信号结合起来,做成一个「动量-反转」复合因子。当动量强时,跟随动量;当动量衰减且反转信号出现时,反向操作。这样既能吃到趋势的肉,又能躲过反转的坑。
4.4 动量与反转的实战框架
说了这么多,怎么落地?我画了一张框架图,把整个逻辑串起来:
这个框架的核心逻辑很简单:先算动量,再判断动量是否衰减。如果衰减了,就去看反转信号是否成立;如果没衰减,就继续跟着动量走。我在实盘中用这套逻辑,年化超额收益大概能提升5-8个百分点。
一个小技巧:动量衰减的阈值不要设死。市场波动大的时候,阈值可以放宽到40%;市场平稳的时候,收紧到20%。说白了,就是要根据市场状态动态调整参数。
最后说一句,动量与反转不是非此即彼的关系。它们就像硬币的两面,关键是你得知道什么时候该翻哪一面。多复盘、多测试,慢慢你就能找到那个节奏感。
公众号:蓝海数据掘金营,微信deep3321