当前位置:首页>python>一文搞懂:用 Python 做市场状态识别的混合机器学习实战

一文搞懂:用 Python 做市场状态识别的混合机器学习实战

  • 2026-10-11 05:59:41
一文搞懂:用 Python 做市场状态识别的混合机器学习实战

用 Python 揭秘均值回归策略:你的收益从何而来?

2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 500 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!

引言

金融市场并非一成不变,它会在「风险偏好」(Risk-on)与「风险规避」(Risk-off)之间不断切换。当波动率飙升、信用利差走阔时,市场往往进入「压力状态」;而当行情平稳、资金涌入高收益资产时,市场则处于「扩张状态」。这些隐藏的「市场状态」(Market Regime)如果能被提前识别,对资产配置、波动率预测和系统化交易都极具价值。

本文将带你走完一套完整的混合机器学习(Hybrid ML)流程:从多资产数据获取、特征工程、PCA 降维、K-Means 聚类,到监督学习分类与可解释性评估。整个流程用 Python 实现,适合想把机器学习落地到金融场景的同学练手。


一、什么是「无监督到监督」的混合框架

传统做法有两条路,各有短板:

  • • 只做聚类(无监督):能发现市场状态,却无法预测未来会进入哪个状态。
  • • 只做分类(监督):能预测,但现实中根本没有「真实状态标签」可供训练。

混合框架巧妙地把两者结合,也被称为半监督状态建模或潜在状态预测,核心思路分两个阶段:

  • • 阶段一(无监督):用 K-Means、高斯混合模型(GMM)、隐马尔可夫模型(HMM)等聚类方法,在没有预定义标签的情况下发现潜在状态。
  • • 阶段二(监督):把聚类得到的标签当作「真值」,再用逻辑回归(Logistic Regression)、随机森林、XGBoost 等模型去预测未来状态。

这样一来,标签由数据自动生成,预测模型也有了训练目标,一举解决了两条老路的痛点。


二、选哪些标的?一篮子「混合宏观 ETF」

本案例特意挑选了一组信号更「混叠」、更难分离的资产组合,用来检验框架的稳健性:

  • • VTI:全市场股票 ETF,反映美国整体股市的风险偏好。
  • • IWO:小盘成长股 ETF,高 Beta、对流动性和货币环境敏感,擅长捕捉「风险偏好上行」行情。
  • • JNK:高收益(垃圾)债 ETF,兼具债性与股性,常作为市场情绪恶化的早期信号。
  • • AGG:综合投资级债券 ETF,是「避险」与「防御性配置」的代表。
  • • VXX:短期 VIX 期货 ETN,在恐慌抛售时反应极快,但长期受期货贴水影响噪声较大。

三、Step 1:拉取行情数据

第一步是获取从 2020 年到 2026 年的历史数据。下面用一个通用函数封装 API 调用:

import pandas as pdimport numpy as npstart_date = "2020-01-02"end_date = "2026-05-23"def fetch_eod(symbol, start_date, end_date):    """获取指定标的的日线行情数据"""    url = f"https://example-data-api.com/eod/{symbol}"    params = {        "from": start_date,        "to": end_date,        "fmt": "json"    }    resp = requests.get(url, params=params)    data = resp.json()    df = pd.DataFrame(data)    df = df.sort_values("date")            # 按日期排序,保证时间顺序    df["date"] = pd.to_datetime(df["date"])    df.set_index("date", inplace=True)     # 把日期设为索引    return df# 分别拉取 5 个标的的数据spy_df = fetch_eod("VTI", start_date, end_date)   # 全市场股票iwm_df = fetch_eod("IWO", start_date, end_date)   # 小盘成长hyg_df = fetch_eod("JNK", start_date, end_date)   # 高收益债lqd_df = fetch_eod("AGG", start_date, end_date)   # 投资级债vix_df = fetch_eod("VXX", start_date, end_date)   # 波动率

小贴士:金融数据建模对数据质量极其敏感。如果数据太短或缺失太多,状态划分会非常不可靠,因此务必先用 df.info() 检查数据完整性。


四、Step 2:特征工程

原始的 OHLCV 数据信息量有限,关键在于构造能反映市场结构的「衍生特征」。

1. 信用利差代理

用高收益债与投资级债的对数价差变化,刻画信用条件的松紧:

df = spy_df.copy().sort_index()# 内连接合并债券价格,只保留共同交易日df = df.join(hyg_df["adjusted_close"].rename("jnk"), how="inner")df = df.join(lqd_df["adjusted_close"].rename("agg"), how="inner")# 信用利差 = 高收益债与投资级债对数价格之差的变化量df["Credit_Spread"] = (np.log(df["jnk"]) - np.log(df["agg"])).diff()df = df.dropna()

2. 多周期收益率

捕捉日内、短期、中期、长期的动量信号:

win_s, win_m, win_l = 15, 50, 150  # 短、中、长期窗口df["SPX_Daily_Return"] = spy_df["close"].pct_change()       # 日收益率df["SPX_21D_Return"]   = spy_df["close"].pct_change(win_s)  # 短期df["SPX_63D_Return"]   = spy_df["close"].pct_change(win_m)  # 中期df["SPX_126D_Return"]  = spy_df["close"].pct_change(win_l)  # 长期

3. 已实现波动率

用指数加权的对数收益率计算年化波动率:

def realized_vol(series, span=21):    """基于指数加权对数收益率计算年化已实现波动率"""    series = series.copy()    series = series.replace([0, np.inf, -np.inf], np.nan)  # 清除无效值    series = series.dropna()    returns = np.log(series).diff()                        # 转为连续收益率    return returns.ewm(span=span).std() * np.sqrt(252)     # 年化(252 个交易日)df["SPX_21D_RealVol"] = realized_vol(df["SPX_Daily_Return"], span=win_s)df["SPX_63D_RealVol"] = realized_vol(df["SPX_Daily_Return"], span=win_m)

此外还可以构造波动率比值、回撤(Drawdown)等特征。最后记得清洗数据并剔除原始价格列,只保留工程化后的数值特征,避免「原始价格偏差」。


五、Step 3:PCA 降维 + K-Means 聚类

特征多了之后往往高度相关,先用 PCA(主成分分析)压缩维度:

from sklearn.decomposition import PCA# 标准化(这里用扩展窗口的均值和标准差,避免未来数据泄漏)X_scaled = (X_clean - X_clean.expanding().mean()) / X_clean.expanding().std()X_scaled = X_scaled.dropna()pca = PCA()X_pca = pca.fit_transform(X_scaled)explained_var = pca.explained_variance_ratio_  # 各主成分的方差贡献率

案例结果显示:约 1314 个主成分就能保留 **95%96%** 的信息,说明原始特征存在大量冗余,降维空间很大。

接着用轮廓系数(Silhouette Score)来挑选最佳聚类数 K(系数越接近 +1 越好):

from sklearn.cluster import KMeansfrom sklearn.metrics import silhouette_scoredef silhouette_over_k(X, k_min=2, k_max=6, seed=42):    """遍历不同 K 值,计算轮廓系数"""    scores = {}    for k in range(k_min, k_max + 1):        km = KMeans(n_clusters=k, n_init=50, random_state=seed)        labels = km.fit_predict(X)        scores[k] = silhouette_score(X, labels)    return scores# 最终选定 K=2,对全数据聚类kmeans = KMeans(n_clusters=2, n_init=50, random_state=42)final_labels = kmeans.fit_predict(X_final)df.loc[valid_idx, "Regime"] = final_labels  # 把状态标签写回原表

虽然轮廓系数只有约 0.18(分离度一般),但通过分组统计,两个状态依然展现出清晰的经济含义:

  • • 状态 0(平静期):日均收益略为正(+0.0023),波动率低,单日涨跌幅较小。
  • • 状态 1(动荡期):日均收益略为负(-0.0023),波动率几乎翻倍,单日波动可达 -5.87% 到 +10.15%。

这说明聚类确实捕捉到了真实的市场风险结构,而非随机分组。


六、Step 4:监督学习分类

把状态标签当作目标变量,训练一个逻辑回归分类器:

from sklearn.model_selection import train_test_splitfrom sklearn.linear_model import LogisticRegressionfrom sklearn.metrics import accuracy_scoreX = X_final                                    # PCA 降维后的特征y = df.loc[df["Regime"].notna(), "Regime"].astype(int).values# 分层抽样划分训练集与测试集X_train, X_test, y_train, y_test = train_test_split(    X, y, test_size=0.4, random_state=42, stratify=y)clf = LogisticRegression(max_iter=500, solver="lbfgs")clf.fit(X_train, y_train)y_pred = clf.predict(X_test)print("准确率:", accuracy_score(y_test, y_pred))  # 约 0.988

测试集准确率高达 98.8%,两个状态的精确率、召回率、F1 分数都在 0.98 以上。这意味着波动率、收益率、信用利差等特征中,蕴含着高度可区分的状态信息。


七、Step 5:可解释性评估(XAI)

光有高准确率还不够,还要让模型「可解释、可信赖」。常用的诊断工具包括:

  • • 混淆矩阵:直观看出误分类发生在哪里。
  • • 学习曲线:训练精度与验证精度收敛且差距小,说明模型泛化良好。
  • • 校准曲线(Calibration Curve):检验预测概率是否可靠。
  • • KS 统计量:本例达到 0.979,表示两类几乎完美分离。
  • • ROC-AUC 曲线:AUC = 1.00,区分能力极强。
  • • 判别阈值分析:得出最佳阈值约 0.57,比默认的 0.50 略保守。
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplayimport matplotlib.pyplot as plt# 计算并可视化混淆矩阵cm = confusion_matrix(y_test, y_pred, labels=[0, 1])disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=[0, 1])disp.plot(cmap=plt.cm.Blues)plt.title("混淆矩阵:状态 0 vs 状态 1")plt.show()

延伸知识:ROC-AUC 衡量整体排序能力,而 KS 统计量关注两类概率分布的最大分离点;在样本不均衡时,精确率-召回率(PR)曲线往往比 ROC 更有参考意义。本案例两个状态样本量为 539 与 906,存在一定不均衡,因此多指标交叉验证尤为重要。


总结

本文完整复现了一套用于市场状态识别的混合机器学习流程,核心要点可以归纳为:

  1. 1. 数据是地基:选取覆盖股票、信用、利率、波动率的多资产标的,并确保数据可靠。
  2. 2. 特征工程是灵魂:把原始价格转化为收益率、波动率、信用利差、回撤等更有信息量的宏观特征。
  3. 3. PCA 解决冗余:少数主成分即可保留绝大部分信息,大幅降低维度。
  4. 4. 聚类发现状态:即便分离度不高,K-Means 仍能识别出经济含义清晰的「平静期」与「动荡期」。
  5. 5. 分类实现预测:用聚类标签训练逻辑回归,可获得极高的预测精度。
  6. 6. 可解释性兜底:借助混淆矩阵、校准曲线、KS 统计量等工具,让模型既强大又透明,契合可解释 AI(XAI)理念。

这套「无监督发现 + 监督预测」的框架,为状态感知的资产配置、波动率预测和系统化交易提供了一个可扩展、可复现的实用范式。无论你是金融从业者还是机器学习爱好者,都值得动手跑一遍。

免责声明:本文内容仅供学习与教育用途,不构成任何投资建议。任何投资决策请结合自身情况并咨询专业人士。


参考文章

加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取本文完整研究解析、代码实现细节

财经数据与量化投研知识社区

2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:

  1. 1. 双典系统赋能:获赠《财经数据宝典》与《量化投研宝典》完整文档,凝练多年实战经验,构建系统化知识框架;
  2. 2. 量化因子日更教程(2026重磅新增):每日更新「量化因子专题教程」,配套完整可运行代码与实战案例,深度拆解因子构建、回测与优化全流程;
  3. 3. 量化文章专题教程库:500+篇星球独有高质量教程式文章,系统覆盖策略开发、因子研究、风险管理等核心领域,内容基本每日更新,并配套精选学习资料与实战参考;
  4. 4. 量化投研实战课程:赠送《AKQuant-入门及实战》《PyBroker-入门及实战》视频课程,手把手教学,快速掌握量化策略开发技能;
  5. 5. 财经数据支持:定期更新国内外财经数据,为策略研发提供精准、可靠的数据基础;
  6. 6. 顶尖学者与行业专家分享:年度邀请学术界博士与业界资深专家开展前沿论文精讲与实战案例分享,不少于4场,直击研究前沿与产业实践;专家直连答疑:与核心开发者及领域专家实时互动,高效解决投研实战难题;
  7. 7. 专业社群与专属福利:加入高质量交流社群,获取课程折扣及更多独家资源。

星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!

好文推荐

1. 用 Python 打造股票预测系统:Transformer 模型教程(一)

2. 用 Python 打造股票预测系统:Transformer 模型教程(二)

3. 用 Python 打造股票预测系统:Transformer 模型教程(三)

4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)

5. 揭秘隐马尔可夫模型:因子投资的制胜武器

6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用

7. 金融 AI 助手:FinGPT 让你轻松掌握市场分析

8. 量化交易秘籍:为什么专业交易员都在用对数收益率?

9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解

10. 掌握金融波动率模型:完整 Python 实现指南

好书推荐

最新文章

随机文章