动量信号生成:时间序列动量、截面动量、动量衰减因子、信号平滑处理

动量信号,说白了就是市场趋势的「温度计」。你想想看,价格涨了还会继续涨吗?跌了还会继续跌吗?动量策略就是赌这个「惯性」。但怎么赌、赌多久、赌多大,这里面的门道可不少。

我个人习惯把动量信号拆成四个维度来理解:时间序列动量、截面动量、动量衰减因子、信号平滑处理。每个维度解决一个核心问题。咱们一个一个聊。

1. 时间序列动量:跟自己的历史比

时间序列动量,也叫「绝对动量」。它只看单个资产自己的历史表现。比如某只股票过去60天涨了15%,那就认为它处于上升趋势,做多。如果跌了10%,就做空。

公式很简单:

Signal_ts = (P_t - P_{t-n}) / P_{t-n}

其中 P_t 是当前价格,P_{t-n} 是 n 天前的价格。

我在项目中遇到过一个问题:直接用收盘价算,结果信号噪声特别大。后来我改用「滚动收益率」,也就是用对数收益率累加,效果好了不少。

我的习惯:回看周期选20天、60天、120天。短周期抓反转,长周期抓趋势。我一般用60天做主信号,20天做辅助。

2. 截面动量:跟同类比

截面动量,也叫「相对动量」。它不看资产自己的涨跌,而是看它在同类资产中的排名。比如全市场5000只股票,过去60天涨得最好的前20%做多,最差的20%做空。

计算步骤:

  1. 计算每个资产的过去n天收益率
  2. 对所有资产的收益率排序
  3. 取排名前20%的做多,后20%的做空
  4. 或者用z-score标准化:Signal_cs = (R_i - mean(R)) / std(R)

嗯,这里要注意:截面动量对「分组数量」很敏感。我曾经试过分5组和分10组,结果年化收益差了将近4%。建议你多跑几组参数看看稳定性。

避坑指南:截面动量在极端行情下容易「踩踏」。2020年3月那次,所有资产一起暴跌,截面动量还在做多「相对跌幅小」的资产,结果照样亏得厉害。我后来加了波动率过滤,才把回撤控制住。

3. 动量衰减因子:让旧信号「过期」

动量信号有个毛病:越老的信号越没用。比如60天前的涨跌,对今天的走势影响已经很小了。所以我们需要一个衰减因子,让旧信号慢慢「失效」。

我常用的两种方式:

  • 指数衰减:权重 = exp(-λ * t),λ控制衰减速度。λ越大,旧信号消失得越快。
  • 线性衰减:权重 = 1 - t/T,T是总回看周期。简单粗暴,但效果不差。

举个例子,60天回看周期,指数衰减λ=0.05:

import numpy as np

def exp_decay_weights(n, lam=0.05):
    t = np.arange(n)
    weights = np.exp(-lam * t)
    return weights / weights.sum()

weights = exp_decay_weights(60, lam=0.05)
# 第1天权重约0.016,第60天权重约0.001

你看,第60天的权重只有第1天的1/16。说白了,最近10天的数据决定了80%的信号。

核心观点:衰减因子不是越陡越好。我踩过坑,λ设太大,信号变得跟白噪声一样,频繁开平仓,手续费都亏没了。建议λ在0.03到0.08之间调参。

4. 信号平滑处理:去掉毛刺

原始动量信号就像心电图,上蹿下跳。直接用它交易,你会被来回打脸。平滑处理就是给信号「去噪」。

我常用的三种平滑方法:

方法 公式 适用场景
简单移动平均 S_t = (S_{t-1} + S_{t-2} + ... + S_{t-m}) / m 快速实现,延迟适中
指数加权移动平均 S_t = α * raw_t + (1-α) * S_{t-1} 对最新数据更敏感
卡尔曼滤波 状态空间模型 噪声大、非线性场景

我个人最常用指数加权移动平均。α取0.1到0.3之间,既能平滑噪声,又不会延迟太多。卡尔曼滤波虽然理论完美,但参数调起来太费劲,我一般只在研究阶段用。

一个小技巧:平滑窗口不要超过动量回看周期的1/5。比如60天动量,平滑窗口用12天以内。否则信号会被「磨平」,趋势都看不出来了。

5. 完整流程:从原始数据到交易信号

咱们把上面四个步骤串起来,看看完整的信号生成流程:

# 伪代码示例
def generate_momentum_signal(prices, lookback=60, decay_lam=0.05, smooth_alpha=0.2):
    # 1. 计算时间序列动量
    raw_ts = (prices[-1] - prices[-lookback]) / prices[-lookback]
    
    # 2. 计算截面动量(假设有多个资产)
    # raw_cs = zscore(all_assets_returns)
    
    # 3. 应用衰减因子
    weights = exp_decay_weights(lookback, lam=decay_lam)
    weighted_ts = np.sum(raw_ts * weights)
    
    # 4. 信号平滑
    smooth_signal = ema(weighted_ts, alpha=smooth_alpha)
    
    return smooth_signal

你看,代码其实不长。但每个参数的选择,背后都是真金白银的教训。

6. 一张图看懂本章核心

下面这张SVG图,把动量信号生成的四个步骤和它们的关系画清楚了。建议你保存下来,写代码时对照着看。

动量信号生成流程 原始价格数据 时间序列动量 截面动量 动量衰减因子 信号合并与加权 信号平滑处理 最终交易信号 图:动量信号生成的四个核心步骤

从图里你能看到,原始数据先分三路处理:时间序列动量、截面动量、衰减因子。然后合并加权,再经过平滑处理,最终输出交易信号。每一步都有参数要调,每一步都可能踩坑。

总结一下:动量信号生成不是简单的「涨了就买」。你要考虑跟谁比(时间序列 vs 截面)、多久的数据有效(衰减因子)、信号太噪怎么办(平滑处理)。这四个环节缺一不可。

好了,这一章的内容就到这。代码和公式都给你了,剩下的就是自己去跑回测、调参数。记住,没有万能的参数组合,只有最适合你策略的那一组。

专注资料整理