基于隐马尔科夫链的市场状态划分
由sywgfuture01创建,最终由sywgfuture01 被浏览 1 用户
用隐马尔科夫模型(HMM)划分沪深300的市场状态
市场不会一直上涨,也不会一直下跌,它总是在"趋势"和"震荡"之间来回切换。如果能提前识别当前市场处于哪种状态,仓位管理和策略选择就会更有依据。本文介绍如何用隐马尔科夫模型(Hidden Markov Model, HMM)对沪深300指数近十年的走势进行状态划分,把每一天都打上"上涨趋势""下跌趋势""宽幅箱体震荡""窄幅箱体震荡"四种标签之一。
为什么用 HMM
市场状态是"隐藏"的:我们看不到一个明确的标签告诉我们今天是趋势还是震荡,只能看到价格、收益率、波动率这些可观测的表现。HMM 正好适合处理这类问题——它假设背后存在一系列不可观测的隐藏状态,这些状态按照一定的转移概率相互切换,而我们看到的价格数据是这些隐藏状态在不同参数下生成的观测值。通过对历史数据拟合模型,就能反推出每个交易日最可能处于哪个隐藏状态,以及状态之间互相转换的概率。
相比人工设定阈值(比如"20日涨幅超过5%算上涨趋势"),HMM 的优势在于状态划分是数据驱动的,并且天然考虑了状态的持续性(今天是上涨趋势,明天大概率还是上涨趋势,而不是逐日孤立判断)。
数据与特征构造
分析对象是沪深300指数(000300.SH),时间范围覆盖 2016-09-08 至 2026-09-08 这十年。为了让 20 日均线、20 日波动率等指标在分析区间起点就有完整数据,实际取数时向前多留了 450 天的缓冲期。
从 BigQuant 的 cn_stock_index_bar1d 表中查询以下字段:
close:收盘价ret_20d:20 日收益率(close / m_lag(close, 20) - 1)ma125:125 日均线vol_20d:20 日年化波动率(20 日收益率标准差 × √252)max_dd_63d:63 日最大回撤(1 - close / m_max(close, 63))
HMM 需要的观测向量并不是用全部字段,而是精选了两个最能刻画"方向"和"宽窄"的特征:
- 日对数收益率(
ret_1d = ln(close_t / close_{t-1})):刻画涨跌方向 - 20 日年化波动率(
vol_20d):刻画震荡的宽窄
这两个特征组合起来,理论上就能把"趋势 vs 震荡""宽幅 vs 窄幅"四种状态区分开:涨跌方向由收益率均值决定,震荡宽窄由波动率高低决定。构造完特征后,对两列做了标准化(减均值除标准差),避免两个特征量纲不同影响模型拟合。
模型拟合
模型选用 hmmlearn 库的 GaussianHMM,关键参数:
| 参数 | 取值 | 说明 |
|---|---|---|
n_components |
4 | 隐藏状态数,对应四种市场状态 |
covariance_type |
full |
允许两个观测特征之间存在协方差 |
n_iter |
1000 | EM 算法最大迭代次数 |
tol |
1e-4 | 收敛阈值 |
HMM 的 EM 拟合容易陷入局部最优,因此代码用 8 个不同的随机种子分别拟合模型,取对数似然(log-likelihood)最高的一个作为最终模型。这次拟合中最优对数似然为 -3974.50。
拟合完成后,用 model.predict() 对每个交易日做 Viterbi 解码,得到最可能的隐藏状态序列。
隐藏状态如何映射成"市场状态"名称
HMM 输出的隐藏状态只是编号(0、1、2、3),没有业务含义,需要人工映射。映射规则是:
- 按各隐藏状态下的日收益率均值排序:收益均值最高的状态 → 上涨趋势;最低的 → 下跌趋势
- 剩下两个状态按20日波动率均值排序:波动率高的 → 宽幅箱体震荡;波动率低的 → 窄幅箱体震荡
本次拟合得到的四个隐藏状态特征如下:
| 隐藏状态 | 日收益率均值 | 20日波动率均值 | 映射结果 |
|---|---|---|---|
| 0 | 0.000676 | 0.096084 | 上涨趋势 |
| 1 | -0.000145 | 0.288378 | 下跌趋势 |
| 2 | -0.000055 | 0.174791 | 宽幅箱体震荡 |
| 3 | -0.000093 | 0.136313 | 窄幅箱体震荡 |
可以看到,下跌趋势状态的波动率(0.288)明显高于其他三个状态——这符合直觉,市场下跌时往往伴随更剧烈的波动。
十年状态分布:四分天下
把 2016-09-08 至 2026-09-08 这段区间(共 2427 个交易日)按上述规则打标签,统计结果相当均衡:
| 市场状态 | 天数 | 占比 |
|---|---|---|
| 上涨趋势 | 570 天 | 23.5% |
| 下跌趋势 | 547 天 | 22.5% |
| 宽幅箱体震荡 | 676 天 | 27.9% |
| 窄幅箱体震荡 | 634 天 | 26.1% |
四种状态的占比都在 22%~28% 之间,没有哪一种状态明显占主导——这说明沪深300在过去十年确实是趋势和震荡交替出现,"单边市"和"磨人的震荡市"大致各占一半时间。
各状态的详细特征
上涨趋势
- 出现 18 次,平均每次持续 31.7 天
- 状态内累计收益 +44.25%
- 20日收益率均值 1.26%,波动率均值 9.6%(四个状态中波动最低)
- 63日最大回撤仅 11.89%,是最"舒服"的状态
下跌趋势
- 出现 15 次,平均每次持续 36.5 天(持续时间最长的状态)
- 20日波动率均值高达 27.9%,是上涨趋势的近 3 倍
- 63日最大回撤达 21.64%,风险最高
- 值得注意的是该状态下"状态内收益"统计为 +21.44%——这是因为区间起止点价格差异导致的统计口径问题,不代表下跌趋势中每天都在涨,实际上该状态下 20 日收益率均值为 -0.31%,且波动极大(标准差 7.74%,最小值 -14.58%),本质仍是剧烈下跌夹杂反弹的状态
宽幅箱体震荡
- 出现次数最多(33 次),但平均持续时间较短(20.5 天)
- 波动率均值 17.46%,介于上涨和下跌趋势之间
- 63日最大回撤 16.87%
窄幅箱体震荡
- 出现 37 次,平均持续 17.1 天,是切换最频繁、单次持续最短的状态
- 波动率均值 13.63%,是四个状态中除上涨趋势外最低的
- 这类状态往往对应大盘"磨底"或"高位横盘"阶段
状态转移矩阵:状态一旦形成就很"粘"
HMM 的状态转移矩阵揭示了各状态之间的切换概率:
上涨趋势 => 上涨趋势:96.42% 下跌趋势:0.20% 宽幅震荡:0.23% 窄幅震荡:3.15%
下跌趋势 => 上涨趋势:0.16% 下跌趋势:97.29% 宽幅震荡:2.55% 窄幅震荡:0.00%
宽幅箱体震荡 => 上涨趋势:0.00% 下跌趋势:1.65% 宽幅震荡:94.49% 窄幅震荡:3.86%
窄幅箱体震荡 => 上涨趋势:2.75% 下跌趋势:0.38% 宽幅震荡:3.39% 窄幅震荡:93.48%
四个状态的自转移概率都在 94%~97% 之间,也就是说市场状态一旦形成,次日大概率延续原状态,这正是 HMM 相比逐日孤立判断的核心优势——它把"状态具有持续性"这一先验知识内嵌到了模型结构里。
从跨状态转移路径看也符合市场直觉:
- 上涨趋势 主要转向 窄幅箱体震荡(3.15%),几乎不会直接跳到下跌趋势
- 下跌趋势 主要转向 宽幅箱体震荡(2.55%),同样几乎不会直接反转为上涨趋势
- 宽幅箱体震荡 主要转向 窄幅箱体震荡(3.86%)或 下跌趋势(1.65%)
- 窄幅箱体震荡 主要转向 上涨趋势(2.75%)或 宽幅箱体震荡(3.39%)
也就是说,趋势状态之间几乎不会"硬切换",中间大多会经过震荡状态过渡——这与真实市场"见顶/见底后先横盘再变盘"的经验规律是一致的。
可视化呈现
最终结果通过 Plotly 绘制成一张交互式图表:以沪深300累计涨跌幅为纵轴、日期为横轴,用四种颜色的折线段标注每一天所处的市场状态(红色=上涨趋势,绿色=下跌趋势,黑色=宽幅箱体震荡,黄色=窄幅箱体震荡),并支持鼠标悬停查看具体日期、收盘价和状态。图表导出为独立的 HTML 文件,可以直接在浏览器中打开查看,方便回溯任意历史时点的市场状态归属。
如果数据截止到今天的话,我们还可以看市场状态的判断:(目前市场 96.42%的概率是上涨)
\
小结
这套 HMM 状态划分方法的核心思路可以概括为三步:选取能同时反映方向和波动的观测特征 → 用多次随机初始化的 GaussianHMM 拟合隐藏状态 → 依据状态的统计特征(收益率、波动率)给隐藏状态赋予业务含义。相比人工规则,这种方法的状态划分边界是模型从数据中学出来的,且自带的转移概率矩阵天然刻画了状态的持续性和切换路径,可以作为仓位管理、策略切换的参考信号。
需要注意的是,HMM 是基于历史数据拟合的无监督模型,状态划分本质上是"事后归因",实盘中用于判断"当前"状态时会存在一定的滞后性(模型需要看到近期一段窗口的收益和波动数据才能给出较稳定的状态判断),使用时需结合其他确认信号,不宜单独作为交易决策依据。
代码见:
分享链接: https://bigquant.com/codesharev3/e5b6a302-adca-4589-95a3-123337e62523 密码请联系 小q
\
\