熵率市场状态识别:用信息熵把『混沌』和『秩序』分开
趋势市和随机游走市,在统计上到底差在哪?答案不是『涨跌多少』,而是『有多少可预测的结构』。信息熵(Shannon 熵 + 置换熵)把收益分布的『胖瘦』和『先后次序』量化成一个 0~1 的数字:趋势段熵低(有秩序),随机段熵高(混沌)。滑窗滚动算出来就是一条『市场状态』序列,能当过滤器——只在低熵(有秩序)时段持仓,Sharpe 从 0.36 拉到 4.68。附完整 Python 与六类真实陷阱(高阶)。
你盯着一张 K 线图,能一眼分出「这段在走趋势」和「这段在随机乱晃」。但机器怎么分?一个朴素的想法是用波动率:趋势市波动大、随机市波动小,或者反过来。可惜波动率对两者几乎是瞎的——一段匀速上涨(标准趋势)可以波动极低,一段剧烈无序震荡(标准混沌)也可以波动极高,两者在波动率轴上大量重叠。
真正的区别不在「幅度」,在「结构」。趋势市的收益序列有秩序:方向可预测、排列有规律;随机市的收益序列是白噪声:每一步独立、毫无记忆。信息论里早就有一个衡量「结构有多少」的工具——熵。本文用两种熵把「混沌」和「秩序」钉成两个可计算的数字,再把它变成一条能直接当过滤器用的状态序列。
一、为什么用熵:波动率是瞎的,熵是亮的#
熵(entropy)度量一个分布的「不确定性」或「无序程度」:越确定、越有结构,熵越低;越随机、越混乱,熵越高。我们不直接对价格算熵,而是先对收益或对数价格的局部形态做量化,再算熵。
两种熵,侧重点不同:
- Shannon 熵:看收益分布有多「胖」多「散」。把日收益按固定分箱,算每个箱概率
p_i,归一化熵H = -Σ p_i·log2(p_i) / log2(箱数)。趋势市收益集中在某一侧(分布窄),熵低;随机市收益均匀铺开(分布胖),熵高。 - 置换熵(Bandt-Pompe 2002):不只看「分布」,还看时间先后次序。对对数价格做嵌入,把每一小段映射成一种「序数模式」(谁最大谁最小),数各种模式出现的频率再算熵。趋势市的序数模式高度集中(总是「低→高」或「高→低」),熵低;随机市的序数模式均匀乱跳,熵高。
直觉一句话:Shannon 量「分布胖瘦」,置换熵还量「次序规律」——所以它对趋势更敏感。
import numpy as np
def rolling_shannon(r, edges, w):
"""滑动窗口 Shannon 熵(收益分布), 归一化到 0~1"""
nb = len(edges) - 1
H = np.full(len(r), np.nan)
for t in range(w, len(r)):
hist, _ = np.histogram(r[t - w:t], bins=edges)
p = hist / hist.sum()
p = p[p > 0]
h = -np.sum(p * np.log2(p)) / np.log2(nb)
H[t] = h
return H
def permutation_entropy(ts, m=3, w=60, tau=1):
"""Bandt-Pompe 置换熵: 量化局部时间次序"""
import math
n_pat = math.factorial(m)
PE = np.full(len(ts), np.nan)
for t in range(w, len(ts)):
seg = ts[t - w:t]
counts = np.zeros(n_pat)
for i in range(len(seg) - (m - 1) * tau):
pat = seg[i:i + m * tau:tau]
order = np.argsort(np.argsort(pat)) # 序数模式
idx = 0
for i2 in range(m): # Lehmer 编码 -> 唯一索引
smaller = sum(1 for j in range(i2 + 1, m) if order[j] < order[i2])
idx = idx * (m - i2) + smaller
counts[idx] += 1
p = counts[counts > 0] / counts.sum()
pe = -np.sum(p * np.log(p)) / np.log(n_pat)
PE[t] = pe
return PEpython二、合成一个「秩序 / 混沌」交替的市场#
为了看清熵到底在量什么,造一段 1500 天、6 段交替的行情:偶数段是趋势市(小幅漂移 + 低噪声),奇数段是随机市(无漂移 + 高噪声)。
rng = np.random.default_rng(20260716)
N_DAYS, SEG = 1500, 250
sigma_low, sigma_high = 0.003, 0.020
ret = np.zeros(N_DAYS); regime = np.zeros(N_DAYS, dtype=int)
for s in range(N_DAYS // SEG):
a, b = s * SEG, (s + 1) * SEG
if s % 2 == 0:
drift = 0.0020 * (1 if (s // 2) % 2 == 0 else -1) # 趋势市
ret[a:b] = drift + sigma_low * rng.standard_normal(b - a)
regime[a:b] = 0
else:
ret[a:b] = sigma_high * rng.standard_normal(b - a) # 随机市
regime[a:b] = 1
price = 100 * np.exp(np.cumsum(ret))python滑窗 W=60 天,分别算 Shannon 熵和置换熵:

绿底=趋势市,红底=随机市。注意熵率序列完美贴着两种体制切换:趋势段两条熵都趴在低位,随机段都弹到高位。这就是「把混沌和秩序分开」的机器版本——不用看价格,光看熵率就知道现在处在哪种体制。
三、两种熵,谁更灵敏?#
把两条熵率单拎出来对比就清楚了:

- Shannon 熵只看「分布胖瘦」。趋势市因为低噪声、收益挤在漂移附近,分布确实窄,熵低;但它对漂移方向无感。
- 置换熵还看「先后次序」。趋势市的序数模式高度一致(一路低→高或高→低),所以哪怕波动不大,它的「次序集中度」也极高、熵更低,对趋势的区分度通常比 Shannon 更干净。
实测两段分布的均值(归一化熵):
| 体制 | Shannon 熵 | 置换熵 |
|---|---|---|
| 趋势段(秩序) | 0.534 | 0.776 |
| 随机段(混沌) | 0.928 | 0.928 |

趋势段的 Shannon 均值(0.53)和随机段(0.93)几乎不重叠——分离度很好。置换熵的区分稍弱(趋势段 0.78 vs 随机段 0.93),说明在我们的设定里次序信息被高噪声部分掩盖,但两者都显著可分。实操中常把两个熵拼成一个特征,比单用一个稳。
四、把熵率当过滤器:只在「有秩序」时持仓#
熵率最值钱的用法不是画图,是当交易开关。逻辑很朴素:在低熵(有秩序、有趋势)的时段持有方向仓,在高熵(混沌、噪声主导)的时段空仓——相当于只做「市场自己已经走出结构」的那部分。
用 20 日动量做信号,仅在熵率低于 40% 分位(即「秩序最强」的时段)才持仓:
look = 20
mom = np.zeros(N_DAYS)
for t in range(look, N_DAYS):
mom[t] = np.sign(np.sum(ret[t - look:t])) # 动量方向
thr = np.nanpercentile(H_shannon[~np.isnan(H_shannon)], 40)
pos_filtered = np.where(H_shannon < thr, mom, 0.0) # 高熵时段强制空仓
eq_un = np.cumprod(1 + mom * ret) # 不过滤
eq_fi = np.cumprod(1 + pos_filtered * ret) # 低熵过滤python
结果刺眼:不过滤的动量 Sharpe 只有 0.36(随机市里反复被打脸),而只在低熵时段持仓的版本 Sharpe 冲到 4.68。差距不是来自多赚,而是来自少亏——高熵随机段本来就是动量的坟场,熵过滤器把这部分直接剔掉了。
提醒:4.68 是合成数据里「体制标签完美 ⇒ 过滤器近乎作弊」的结果,实战绝不会有这种数字。它的真正价值是证明熵率携带了可被交易的结构信息,而不是给你一个能实盘的圣杯。
五、真实陷阱(别直接照抄)#
-
窗口长度 W 是玄学参数。W 太短→熵被噪声主导、抖动剧烈;W 太长→状态切换滞后、等熵反应过来行情已走完。要对 W 做稳健性扫描,别只报一个「最好看」的窗口。
-
分箱边界决定 Shannon 熵的绝对值。箱太多→每个箱样本少、熵被高估;箱太少→抹掉结构、熵被低估。固定分箱在波动率 regime 切换时会漂移,建议用自适应分箱(按滚动分位数切)。
-
置换熵的嵌入维 m 与延迟 τ 要选。m 太小(如 2)区分力弱,m 太大需要更长窗口才统计得稳;τ 跳过相关性也跳过信息。这俩是另一个需要调的参数对,不是免费的。
-
熵是「状态描述」不是「方向预测」。低熵只告诉你「现在结构强」,不告诉你结构是涨是跌——所以上面才要用动量给方向、用熵给「该不该下注」的开关。把熵当方向信号用是常见错误。
-
非平稳市场的伪状态切换。一次性的跳空、流动性事件会让熵瞬时飙升,被误判成「进入混沌」。要加平滑(如对熵率再做 EWMA)或要求连续 K 根确认,避免被单日异常骗。
-
幸存者偏差与过拟合。本文用已知体制造数据,过滤器当然显灵。实盘要样本外滚动验证,并且熵阈值(40% 分位)这种数字必须固化在回测前,不能事后挑。
六、小结#
市场状态识别的本质问题,是「怎么用一个数描述『现在有没有结构』」。波动率量的是幅度、方向量的是偏移,而熵量的是秩序本身——这才是对「趋势 vs 随机」最正交的刻画。
Shannon 熵管「分布胖瘦」,置换熵多管一层「时间次序」,两者拼起来就是一条可计算、可监控、可当开关用的熵率序列。它不预言涨跌,但它能告诉你:此刻市场是在「有秩序地走」,还是在「无记忆地晃」——而前者才值得你下注。
至此,我们从一个能区分混沌与秩序的熵率,走到用波动率刻画风险(GARCH)、用相关网络刻画传染——量化的每一步,都是在给「看不见的市场结构」找一个可计算的替身。