2、信息论基础回顾:熵、条件熵、互信息、KL散度在量化中的应用
说实话,很多做量化的朋友一听到「信息论」三个字就头大。觉得这是通信专业的玩意儿,跟股票交易八竿子打不着。我以前也这么想,直到有一次我在回测一个因子时,发现它的夏普比很高,但实盘就是不行。后来一查,原来是这个因子跟市场噪音的互信息太高了——说白了,它只是在拟合随机波动,而不是真正的信号。
从那以后,我养成了一个习惯:任何因子或策略,先用信息论的指标「过一遍筛子」。今天我们就来聊聊这几个核心概念,以及它们在量化里的实际用法。
核心观点:信息论不是理论玩具,它是量化策略的「体检仪」。熵告诉你数据有多乱,互信息告诉你因子和收益有多「默契」,KL散度告诉你模型有没有跑偏。
2.1 熵:量化「不确定性」的尺子
熵这个概念,最早是香农在通信领域提出的。说白了,它就是衡量一个随机变量「有多难猜」。你想想看,如果一个硬币两面都是正面,那它的熵就是0——因为结果完全确定。但如果是一个公平硬币,正反各50%,那熵就是1比特——因为每次抛之前你都得猜。
在量化里,我经常用熵来评估市场状态。比如,计算沪深300日收益率的熵。如果熵值突然变大,说明市场进入了高不确定性阶段——这时候趋势策略往往失效,而波动率策略反而有机会。
公式很简单:
H(X) = - Σ p(x) * log₂ p(x)
其中 p(x) 是每个可能结果的概率。在量化中,我们通常把收益率离散化成若干个区间,然后统计每个区间的频率。
我的经验:计算熵时,区间数量别选太多。我一般用10-20个等宽区间。区间太多,每个区间的样本太少,熵的估计会偏大——这叫「小样本偏差」。我曾经踩过这个坑,回测时熵值一直异常高,后来才发现是区间设了100个,每个区间只有几十个样本。
2.2 条件熵:加入新信息后,不确定性还剩多少?
条件熵 H(Y|X) 衡量的是:在已知 X 的情况下,Y 还剩多少不确定性。这个指标在量化里特别有用——它可以告诉你,一个因子到底能「解释」掉多少收益的不确定性。
举个例子。假设我们想知道「昨日成交量变化」这个因子,对「今日涨跌」的预测能力。先算今日涨跌的熵 H(Y),再算已知昨日成交量变化后的条件熵 H(Y|X)。如果条件熵明显小于原始熵,说明这个因子确实有用。
公式:
H(Y|X) = - Σ p(x, y) * log₂ p(y|x)
我个人习惯用条件熵来筛选因子。在因子库里有几百个候选因子时,我会先算每个因子对目标收益的条件熵,然后按条件熵从小到大的顺序排序——条件熵越小,说明这个因子越「有料」。
注意:条件熵只能衡量线性或非线性的「整体」关系,但它不告诉你关系是正还是负。所以条件熵低的因子,可能是个反向指标。我一般会结合相关系数一起看。
2.3 互信息:因子与收益的「默契度」
互信息 I(X;Y) 是条件熵的「镜像」——它衡量的是 X 和 Y 共享的信息量。公式是:
I(X;Y) = H(Y) - H(Y|X)
说白了,互信息就是「知道 X 之后,Y 的不确定性减少了多少」。这个指标比相关系数更强大,因为它能捕捉非线性关系。比如,一个因子和收益可能是 U 型关系——因子值在两端时收益高,中间时收益低。相关系数可能接近0,但互信息会很高。
我在项目中遇到过这样一个案例:一个技术指标和未来5日收益的相关系数只有0.03,但互信息却很高。后来仔细分析,发现这个指标在极端行情下特别有效,但在震荡市里完全没用。相关系数被「平均」掉了,但互信息抓住了这个非线性模式。
计算互信息时,我推荐用 sklearn.feature_selection.mutual_info_regression 这个函数。它基于k近邻估计,对连续变量效果不错。
from sklearn.feature_selection import mutual_info_regression
import numpy as np
# 假设 factor 是因子值,returns 是未来收益
factor = np.random.randn(1000)
returns = factor**2 + 0.5 * np.random.randn(1000) # 非线性关系
mi = mutual_info_regression(factor.reshape(-1, 1), returns)
print(f"互信息值: {mi[0]:.4f}")
避坑指南:互信息对样本量敏感。样本太少时,估计值会偏高。我一般要求每个因子至少有500个样本,否则结果不可靠。另外,互信息的值没有上限,所以不同因子之间的互信息值不能直接比较——需要归一化。
2.4 KL散度:检测「分布漂移」的利器
KL散度(Kullback-Leibler divergence)衡量的是两个概率分布之间的差异。公式:
D_KL(P || Q) = Σ p(x) * log₂(p(x) / q(x))
在量化里,KL散度最常见的用途是检测市场状态的变化。比如,我们拿过去3个月的收益率分布作为 P,过去1个月的收益率分布作为 Q。如果 KL散度突然变大,说明市场结构发生了显著变化——这时候你的策略可能需要重新校准。
我记得有一次,一个多因子模型在2020年3月突然失效。我算了一下训练集和测试集的KL散度,发现从0.05飙到了0.8。后来一看,原来是市场从正常状态切换到了极端波动状态,因子的分布完全变了。从那以后,我每次上线策略前,都会设一个KL散度的监控阈值——超过0.3就报警。
计算KL散度时,要注意分布必须要有相同的支撑集。也就是说,P和Q的概率不能一个为0一个不为0。否则KL散度会变成无穷大。我一般会用拉普拉斯平滑来处理:
import numpy as np
def kl_divergence(p, q, eps=1e-10):
# 拉普拉斯平滑,避免除零
p = p + eps
q = q + eps
p = p / p.sum()
q = q / q.sum()
return np.sum(p * np.log2(p / q))
# 示例:两个收益率分布
p = np.array([0.1, 0.2, 0.4, 0.2, 0.1]) # 正常市场
q = np.array([0.3, 0.3, 0.2, 0.1, 0.1]) # 极端市场
print(f"KL散度: {kl_divergence(p, q):.4f}")
2.5 知识体系总览
下面这张图,是我自己整理的信息论在量化中的应用框架。你可以把它当作一个「检查清单」——每次开发新策略时,对照着过一遍。
2.6 实战中的组合用法
在实际项目中,我不会只用单一指标。我有一套固定的流程:
- 先用互信息筛选因子:从几百个候选因子中,挑出互信息排名前20的。
- 再用条件熵排序:对这20个因子,计算它们对收益的条件熵,进一步确认解释力。
- 最后用KL散度做监控:策略上线后,每天计算当前市场分布与训练集分布的KL散度。一旦超过阈值,就触发模型重训练。
这套流程我用了三年,效果还不错。至少帮我避开了好几次「因子失效」的大坑。
一句话总结:熵是「体检」,互信息是「面试」,KL散度是「日常监控」。三者配合,你的策略就不会轻易「翻车」。