
用 Python 揭秘均值回归策略:你的收益从何而来?
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 500 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
引言
你有没有想过这样一个怪现象:某家公司明明业绩超预期,股价却暴跌;另一家公司业绩不及预期,股价反而一路飙升。这背后到底藏着什么秘密?
最近读到一篇非常有意思的研究论文——《Predicting Overnight Stock Changes Post-Earnings》。来自 Alina Hota 等人的研究团队用机器学习方法,分析了 2007 到 2024 年间超过 2700 场财报电话会议的文字稿,最终实现了 58.2% 的方向预测准确率。
听起来好像不高?但在金融领域,比随机猜测高 8 个百分点就足以构建出夏普比率达到 1.67 的交易策略。今天就带大家用 Python 视角拆解这个项目,看看 NLP 与机器学习如何在金融领域落地。
一、为什么财报「话术」比数字更重要?
传统认知里,业绩超预期 = 股价上涨,业绩不及预期 = 股价下跌。但在今天的市场里,这个公式早就失灵了。
研究团队发现:财报电话会议中管理层使用的语言,比财报数字本身更能预测隔夜股价变动。
具体来说,他们关注的是「收盘到开盘」这段窗口期。这段时间没有交易,市场只能消化信息——包括数字背后的「叙事」。
二、核心思路:三层特征工程
研究团队的方法论可以总结为三个关键点:
1. 指引意外指标:捕捉预期收入与实际收入之间的差距 2. 领域专用 NLP:使用 Loughran-McDonald 金融词典 3. 自定义词重要性分析:识别历史上真正驱动隔夜回报的特定词汇
为什么不用通用情感词典?因为金融领域有自己的「黑话」。比如「liability」(负债)在普通英语里是负面的,但在金融里只是中性的会计科目。
下面用 Python 模拟一下这个核心思路:
# 使用 Loughran-McDonald 金融词典做情感分析import pandas as pdfrom collections import Counterimport re# 模拟一个简化版的 LM 词典lm_dictionary = { "positive": ["best", "accomplish", "innovativeness", "growth", "strong"], "negative": ["indict", "abandon", "default", "decline", "loss"], "uncertainty": ["approximate", "almost", "contingency", "maybe"], "strong_modal": ["always", "definitely", "never", "must"], "weak_modal": ["could", "might", "almost", "perhaps"],}def analyze_earnings_call(text): """分析财报电话会议文本的情感特征""" # 文本预处理:转小写并分词 words = re.findall(r"\b\w+\b", text.lower()) word_count = len(words) # 统计各类情感词数量 features = {} for category, word_list in lm_dictionary.items(): count = sum(1 for w in words if w in word_list) features[f"lm_{category}_count"] = count # 归一化:避免长文本的偏差 features[f"lm_{category}_ratio"] = count / word_count if word_count else 0 # 计算净情感得分 features["net_sentiment"] = ( features["lm_positive_count"] - features["lm_negative_count"] ) return features# 示例:分析一段财报发言sample_text = """We had a strong quarter with significant growth. We will definitely accomplish our goals, although there might be some uncertainty in the supply chain."""result = analyze_earnings_call(sample_text)for key, value in result.items(): print(f"{key}: {value}")三、踩过的坑:一个 16 个百分点的教训
这是论文里最有价值的部分。研究团队最初的模型准确率高达 74%,看起来好得不真实。后来发现——确实不真实,是数据泄露惹的祸!
经过严格的时间切分修正后,准确率掉到了 58%。这就是「诚实的代价」。
主要修复了三件事:
• 时间切分:训练数据严格限定在 2007 到 2023 年,测试数据用 2023 到 2024 年 • 去除未来特征:移除任何使用了「全文档统计」的 NLP 特征 • 正确的交叉验证:堆叠模型只能使用「out-of-fold」预测
下面演示如何用 Python 做时间序列的严格切分:
import pandas as pdfrom sklearn.model_selection import TimeSeriesSplit# 加载财报数据(示例)def split_data_by_time(df, split_date="2023-08-01"): """按时间严格切分训练集和测试集,避免数据泄露""" # 确保日期列为 datetime 类型 df["call_date"] = pd.to_datetime(df["call_date"]) # 严格的时间切分:训练集只用过去的数据 train_set = df[df["call_date"] < split_date].copy() test_set = df[df["call_date"] >= split_date].copy() print(f"训练集样本数: {len(train_set)}") print(f"测试集样本数: {len(test_set)}") print(f"训练集时间范围: {train_set['call_date'].min()} 到 {train_set['call_date'].max()}") print(f"测试集时间范围: {test_set['call_date'].min()} 到 {test_set['call_date'].max()}") return train_set, test_set# 五折时间序列交叉验证def time_series_cv_demo(X, y, n_splits=5): """演示时间序列交叉验证,防止未来信息泄露""" tscv = TimeSeriesSplit(n_splits=n_splits) for fold_idx, (train_idx, val_idx) in enumerate(tscv.split(X)): print(f"第 {fold_idx + 1} 折:") print(f" 训练索引范围: {train_idx[0]} 到 {train_idx[-1]}") print(f" 验证索引范围: {val_idx[0]} 到 {val_idx[-1]}")四、模型架构:堆叠集成的「负权重」奇迹
研究团队没有简单地选一个最好的模型,而是构建了一个两层系统:
1. 分类层:预测涨还是跌(方向) 2. 回归层:预测涨跌幅度(量级)
回归层用了三个基模型:Ridge 回归、XGBoost、LightGBM,然后用元学习器组合它们。
这里有个超有意思的发现——元学习器给 XGBoost 分配了负权重!
为什么?因为 XGBoost 和 LightGBM 的预测误差是反相关的。当 XGBoost 高估时,LightGBM 倾向于低估,反之亦然。负权重把 XGBoost 变成了 LightGBM 的「修正信号」。
下面用 Python 实现这个堆叠集成:
import numpy as npfrom sklearn.linear_model import Ridgefrom sklearn.model_selection import KFoldimport xgboost as xgbimport lightgbm as lgbclass StackedEnsemble: """堆叠集成模型:用元学习器组合多个基模型""" def __init__(self, n_folds=5): self.n_folds = n_folds # 三个基模型 self.ridge = Ridge(alpha=1.0) self.xgb_model = xgb.XGBRegressor(max_depth=4, n_estimators=100) self.lgb_model = lgb.LGBMRegressor(num_leaves=31, n_estimators=100) # 元模型:另一个 Ridge 回归 self.meta_model = Ridge(alpha=1.0) def fit(self, X, y): """训练堆叠集成模型""" kf = KFold(n_splits=self.n_folds, shuffle=False) # 存储 out-of-fold 预测,作为元模型的输入特征 oof_predictions = np.zeros((len(X), 3)) for train_idx, val_idx in kf.split(X): X_train, X_val = X[train_idx], X[val_idx] y_train = y[train_idx] # 在每一折上训练基模型 self.ridge.fit(X_train, y_train) self.xgb_model.fit(X_train, y_train) self.lgb_model.fit(X_train, y_train) # 用验证集生成 out-of-fold 预测 oof_predictions[val_idx, 0] = self.ridge.predict(X_val) oof_predictions[val_idx, 1] = self.xgb_model.predict(X_val) oof_predictions[val_idx, 2] = self.lgb_model.predict(X_val) # 元模型学习如何组合三个基模型 self.meta_model.fit(oof_predictions, y) # 输出学到的权重,可能会有负值 print(f"Ridge 权重: {self.meta_model.coef_[0]:.3f}") print(f"XGBoost 权重: {self.meta_model.coef_[1]:.3f}") print(f"LightGBM 权重: {self.meta_model.coef_[2]:.3f}") # 用全部数据重新训练基模型,用于最终预测 self.ridge.fit(X, y) self.xgb_model.fit(X, y) self.lgb_model.fit(X, y) return self def predict(self, X): """生成集成预测""" # 收集三个基模型的预测 base_preds = np.column_stack([ self.ridge.predict(X), self.xgb_model.predict(X), self.lgb_model.predict(X), ]) # 用元模型组合 return self.meta_model.predict(base_preds)五、给 Python 学习者的 5 个关键启示
读完这个项目,我总结了几条对所有数据科学学习者都很有价值的经验:
1. 数据泄露是隐形杀手
特别是在金融领域,任何「偷看未来」的特征都会让模型产生虚假的好成绩。一定要养成「时间感」,每个特征都要问:在做这个预测的时刻,这个信息真的可获得吗?
2. 领域专用工具远胜通用工具
研究团队一开始用通用情感词典效果一般,换成 Loughran-McDonald 金融词典后立刻有了提升。在专业领域工作时,请务必寻找行业专用资源。
3. 集成方法能榨出额外性能
单个模型表现都差不多(53% 到 57%),但堆叠后达到了 58.2%。元学习器能找到单一模型发现不了的模式。
4. 方向准确率比预测误差更重要
在交易场景下,你不需要精确预测涨幅是 5% 还是 5.3%,只要知道涨还是跌就够了。这种业务导向的评估指标设计非常关键。
5. 相关性不等于预测力
研究团队发现 forward_outlook_score 这个指标在探索性分析里和回报相关性很高,但加进模型反而让性能下降。原因是训练集里所有 CEO 都在「过度乐观」,这个特征区分不了真假信心。
# 演示如何评估「方向准确率」这个业务指标import numpy as npdef directional_accuracy(y_true, y_pred): """计算方向预测准确率 在交易场景下,预测对方向比预测精确数值更重要 """ # 同号即为方向预测正确 correct = np.sign(y_true) == np.sign(y_pred) accuracy = np.mean(correct) return accuracy# 示例:实际回报与预测回报y_true = np.array([0.02, -0.01, 0.05, -0.03, 0.01])y_pred = np.array([0.01, -0.02, 0.03, 0.01, 0.02])acc = directional_accuracy(y_true, y_pred)print(f"方向预测准确率: {acc:.2%}")# 输出:方向预测准确率: 80.00%总结
这篇研究最打动我的,不是 58.2% 这个数字本身,而是研究团队呈现的整个工程化思维:
• 诚实地面对数据泄露,主动把准确率从 74% 降到 58% • 承认失败的特征,把 forward_outlook_score 从模型里拿掉 • 用业务指标而非技术指标衡量价值
对 Python 学习者来说,这个项目展示了一条完整的机器学习落地路径:从数据采集(WRDS 多源数据链接),到特征工程(NLP 情感分析),再到模型集成(Stacking),最后到业务应用(交易策略 / 风险管理 / 投研分析)。
最后送大家一句话:「干净的数据胜过聪明的算法」。把数据管道做扎实,比花哨的模型架构有价值得多。
如果你也在学 Python 和机器学习,不妨找一个真实的领域问题,从头到尾走一遍这个流程。你会比刷 100 道 LeetCode 学到更多。
参考文章
财经数据与量化投研知识社区
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
1. 双典系统赋能:获赠《财经数据宝典》与《量化投研宝典》完整文档,凝练多年实战经验,构建系统化知识框架; 2. 量化因子日更教程(2026重磅新增):每日更新「量化因子专题教程」,配套完整可运行代码与实战案例,深度拆解因子构建、回测与优化全流程; 3. 量化文章专题教程库:500+篇星球独有高质量教程式文章,系统覆盖策略开发、因子研究、风险管理等核心领域,内容基本每日更新,并配套精选学习资料与实战参考; 4. 量化投研实战课程:赠送《AKQuant-入门及实战》《PyBroker-入门及实战》视频课程,手把手教学,快速掌握量化策略开发技能; 5. 财经数据支持:定期更新国内外财经数据,为策略研发提供精准、可靠的数据基础; 6. 顶尖学者与行业专家分享:年度邀请学术界博士与业界资深专家开展前沿论文精讲与实战案例分享,不少于4场,直击研究前沿与产业实践;专家直连答疑:与核心开发者及领域专家实时互动,高效解决投研实战难题; 7. 专业社群与专属福利:加入高质量交流社群,获取课程折扣及更多独家资源。
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
好文推荐
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
好书推荐


