先说个很多研究者和量化交易员都绕不过去的场景:做实证分析时,想给“投资者情绪”找个靠谱的度量,翻遍公开数据源,要么只有某个大盘指数,要么是零散的月度调查问卷,完全没法对齐到上市公司层面。结果论文里只能拿一个总指数去解释个股表现,自己心里都发虚。所以我一直觉得,一份按公司-年份排列的投资者情绪面板数据,才是真正能落地的研究起点。最近正好在整理2007-2024年上市公司投资者情绪相关的面板数据,从指标含义、构建逻辑到清洗实操和回归陷阱,这里尽量一次讲透,给需要的人省点弯路。
这份数据的核心价值在于,它把“情绪”这个模糊概念拆成了可比较、可回归、可分组的数字。接下来我就从数据本身的长什么样开始,逐步讲到怎么用、怎么算、怎么避坑。
1. 从原始数据到面板数据:投资者情绪到底记录了什么
1.1 面板数据的基本结构
所谓“面板数据”,简单说就是“多个对象”和“多个时间点”交叉出来的矩形数据块。对上市公司投资者情绪来说,每一行通常代表“某公司在某一年度的情绪状态”,列包含公司标识、年份,以及若干个情绪代理指标或最终合成指数。
我实际整理时,一般会保留以下核心字段:
| 字段名 | 含义 | 典型类型 |
|---|---|---|
| ticker | 证券代码 | 字符型,如“000001”(注意前导零) |
| year | 数据所属年份 | 数值型,如2020 |
| short_turnover | 年度平均换手率 | 小数,常按%计 |
| return_vol | 年度日收益率标准差(波动率) | 小数 |
| trade_volume | 年度成交额对数 | 小数 |
| analys_rating | 分析师评级均值或评级调整 | 数值,有时转化为变化方向 |
| sentiment_index | 合成投资者情绪得分 | 标准化Z分值 |
| sentiment_category | 情绪分组(乐观/中性/悲观) | 分类,如1/0/-1 |
如果你拿到的数据里已经给出了final指数,那可以直接用。如果没有,就需要利用上述基础代理指标自己合成。大多数公开或商业数据集的“投资者情绪面板”,本质就是把这些代理指标按一定权重汇总后的标准化结果。
1.2 情绪指标与基本面指标的根本区别
投资者情绪不等于公司基本面好坏。一家公司利润增长20%,情绪指数可能依然很低,因为市场对它期望更高;反过来,一家亏损公司处于热点赛道时,情绪可能被推到极高。所以使用这份数据时,要时刻警惕“情绪”和“业绩”两者混杂。面板数据的优势在于,通过控制公司固定效应和时间固定效应,可以把“公司自身长期基本面”和“市场共同冲击”剥离,剩下可被情绪解释的部分。
比如,在回归方程里你写:
return = beta0 + beta1 * sentiment_index + controls + firm_effect + year_effect这里的firm_effect吸收了个股不随时间变化的内在质量,year_effect吸收了宏观环境、市场制度变迁等共同因素。剩下sentiment_index前面的系数,才可以相对干净地解读为情绪对收益率的边际影响。
2. 数据构建原理:主流投资者情绪指标的算法拆解
2.1 为什么情绪不能用“问卷调查”直接铺满面板
有人会问,既然有现成调研数据,为什么不直接用?问题在于:问卷类的投资者信心指数多是机构层面的、月度总量数据,难以对应到每个上市公司;而且回答率、统计口径常变,做长期面板时连续性很差。因此,学术和实务里更常用的是“市场交易衍生出的代理指标”,这些指标来自交易行为本身,频率高、覆盖广,能按公司拆开。
最常见的代理指标有这几类:
- 换手率类:高换手往往代表过度自信和分歧加大,情绪偏热。
- 波动率类:情绪极端时价格波动也会放大。
- 流动性类:成交额上升,通常是情绪升温。
- 估值偏离类:如市盈率相对历史中枢的偏离,反映乐观/悲观预期。
- 分析师行为类:评级上调/下调比例、盈利预测修正幅度。
所以,一份成熟的投资者情绪面板数据,通常不会只用单一指标,而是通过主成分分析或者等权标准化,把上述指标里共同的情绪成分提取出来。
2.2 主成分合成的简化流程
假设你手上有数据表,里面有换手率(turn)、波动率(vol)、对数成交额(lvol)、市盈率与历史三年均值的偏离度(pe_dev)四个变量。第一步,先对每个变量做去极值、标准化处理,让口径一致:
# 以行业和年度为维度,对原始指标做中位数去极值 def winsorize_series(s, lower=0.025, upper=0.975): q_low = s.quantile(lower) q_high = s.quantile(upper) return s.clip(lower=q_low, upper=q_high)第二步,做年度截面上的主成分分析。因为面板数据横跨多年,如果直接把所有年份混在一起做标准化,会引入年度间水平差异(比如2015年全市场换手率普遍高)。正确做法是:每一年单独排序标准化,或者按行业-年度分组标准化,然后再提取主成分。我习惯对每个年度截面先做rank化,转为均匀分布,再映射到标准正态分位,这样合成指数就不会受个别极端年份影响。
主成分提取的核心逻辑是找到第一个主成分,它代表四个指标共同变动最强的方向。这个方向的得分就是情绪指数:
from sklearn.decomposition import PCA # data是已经做过年度截面标准化的变量矩阵 pca = PCA(n_components=1) sentiment = pca.fit_transform(data).flatten()实际使用时,记得检查第一主成分的方差解释率,一般超过40%就说明情绪信息确实存在,低于30%则要考虑是否该换指标组合。
2.3 行业调整与市值中性化
不同行业天生换手率和波动率就差很远,如果不做中性化,情绪指数很容易变成“行业指数”——比如小盘科技股普遍分值高,银行股普遍分值低。这样研究结论会被行业结构带偏。所以合成情绪指数后,我通常再做一次行业中性化和市值中性化处理:用行业虚拟变量和市值对数对情绪指数回归,取残差作为中性化后的情绪值。
import statsmodels.api as sm reg = sm.OLS(sentiment_neutral_target, sm.add_constant(industry_dummies + market_value_log)).fit() sentiment_adj = reg.resid经过这一步,情绪指数才真正代表“剔除行业特征和规模特征之后,市场对该股票的情绪溢价/折价”。做个股截面回归时,这个调整尤为重要。
3. 数据实战处理:从拿到面板到跑出稳健结果
3.1 第一件必做之事:识别面板的平衡与非平衡
2007年到2024年跨度大,很多公司在期间上市、退市、停牌,所以面板大概率是非平衡的。处理时最忌讳的做法是把缺失年份的公司直接删除,这会造成幸存者偏差,尤其情绪类指标与市场活跃度相关,退市前往往情绪极度低迷,删掉就损失了关键尾部信息。
我一般先检查每家公司出现次数:
df['freq'] = df.groupby('ticker')['year'].transform('count') # 查看只有少量年份的公司,判断是次新股还是数据缺失对于次新股,前几年没有数据很正常,可以保留并让回归软件处理;对于中途缺数据的,要确认是否是长期停牌或数据源遗漏。如果确认数据源遗漏,建议从原始交易数据重新计算,不要留空。
3.2 关键字段对齐:别让ST和上市状态污染情绪值
情绪是交易者对公司未来预期在当期的反应,但那些被特殊处理的股票,交易规则都变了(涨跌幅限制不同,投资者门槛不同),情绪指数会和常规股票完全不可比。因此实际操作中,我会用两条规则过滤:
- 剔除当年被特殊处理的股票(通常代码带标识);
- 剔除当年上市不满60个自然日的新股,避免上市初期过度炒作对换手率等指标的剧烈干扰。
这么做的理由很直接:如果不剔除,回归时候情绪系数会主要由这批极端值驱动,结论代表性存疑。你可以先全样本跑一次,再剔除后跑一次,结果差异往往非常大。
3.3 收益率与情绪的对齐周期
这里有一个很多人爱犯的错:用当年的情绪指数解释当年的股票收益率。逻辑上说不通——情绪是期初或期中的状态,收益率是期末的结果。正确做法是:
- 用t年年报披露后可得的情绪指标,去预测t+1年的收益率;
- 或者至少保证情绪计算截止日早于被解释收益率起始日。
否则就会产生前视偏差(look-ahead bias)。比如用全年平均换手率去对着全年收益回归,其实已经包含了同时段信息。我处理时会把数据拆成“t年情绪”和“t+1年收益”两列,再合并成回归样本。
示例代码:
emotion_df = df[['ticker', 'year', 'sentiment_adj']].copy() ret_df = df[['ticker', 'year', 'annual_return']].copy() ret_df['year'] = ret_df['year'] - 1 # 将t+1年的收益对齐到t年情绪 aligned = emotion_df.merge(ret_df, on=['ticker', 'year'], how='inner')这样回归样本里的year表示情绪所属年,而annual_return字段存的是下一年度收益。
3.4 分组检验:情绪分位的单调性验证
做任何个股面板之前,我先做一步分组看趋势:把情绪指数按行业-年度分成五组或十组,统计每组下一年度收益率的中位数。如果情绪最高组比最低组收益高很多,且中间组呈单调或U型规律,那就说明情绪变量确实含有预测信息。
df['mkt_adj_ret'] = df['annual_return'] - df.groupby('year')['annual_return'].transform('mean') df['group'] = df.groupby('year')['sentiment_adj'].transform( lambda x: pd.qcut(x.rank(method='first'), 5, labels=False) ) summary = df.groupby('group')['mkt_adj_ret'].agg(['mean', 'median', 'count'])看到单调关系后,再进回归,比从一开始就扔变量进去更有底气。
4. 应用场景与模型选择:情绪数据能回答什么
4.1 资产定价中的情绪载荷
最常见的用法是在Fama-French因子模型或者Carhart四因子模型的基础上,加入情绪因子。做法是:每年初按情绪指数分组,做多高情绪组、做空低情绪组,形成零投资组合,月度/年度计算的收益序列就是情绪因子收益率。然后把这个因子放进定价回归,看它对个股收益是否有解释能力。面板数据在这里的作用就是构造分组因子,同时提供个股的情绪暴露度。
回归方程示例:
import statsmodels.formula.api as smf model = smf.ols('excess_return ~ sentiment_load + market_excess + size + value + momentum + Q_mkt_adj', data=panel).fit()其中excess_return是个股收益减无风险收益率,sentiment_load是前面因子模型得到的时间序列回归系数(通常用滚动窗口估计)。这个系数可以理解成个股对市场整体情绪波动的敏感度。
4.2 投资组合构建中的情绪择时逻辑
量化实践中,情绪面板数据可以用来对组合进行风控:比如当某只股票的情绪指数处于历史极高位置时,说明乐观情绪透支严重,可以降低该股票权重或者加入反转约束。反之,情绪冰点时若基本面并未恶化,可能是一个逆向加仓的候选。
具体操作上,我喜欢对每个公司历史情绪值做滚动分位:
df['his_rank'] = df.groupby('ticker')['sentiment_adj'].transform( lambda x: x.rolling(36, min_periods=12).rank(pct=True) )这个“当前情绪处于历史百分位”比原始数值更直观。当his_rank超过0.9时,标记为高情绪风险股票;低于0.1时标记为低情绪机会股票。把标记用于组合权重再平衡,可以明显降低回撤。注意滚动窗口至少要有三年,否则不稳定。
4.3 行为金融研究:异象解释与过度反应
如果你做纯学术研究,情绪面板最适合用来解释市场异象。比如很多论文发现“低换手率股票未来收益更高”,这个现象实际上可能是情绪的反转作用:高换手率代表情绪过热,定价偏高,随后收益走低。这时你可以引入情绪指数与换手率的交乘项,看情绪能否解释掉换手率的预测能力。面板数据的好处是能控制公司固定效应,同时利用时间维度上的情绪变化来识别效应。
5. 避坑清单:2007-2024这十八年数据里的特别陷阱
5.1 市场制度变化的间断点
从2007到2024,市场经历过多次结构变化:涨跌幅限制调整、融资融券扩容、陆股通开通、注册制试点、交易制度改革等等。这些事件会直接改变交易行为和情绪表达方式。比如涨跌停板制度不同时期对不同股票的约束不一样,换手率、波动率等指标跨制度可比性会下降。
处理办法:关键制度节点加入虚拟变量,或者按制度区间做子样本回归,比较系数差异。如果样本量允许,可以在每一个明显变革年份的前后各做一段回归,观察情绪系数是否突变。这很有现实意义,我在实际分析中确实发现,情绪对收益率的短期反转效应在不同制度区间强度不同。
5.2 极端行情年份要单独审视
2007-2008、2015、2018、2022这些年份,整个市场情绪都处于极端状态。如果不做处理,情绪指数可能被“喇叭口”式的极端值带飞。建议:
- 查看每年的情绪指数分布:某一年如果标准差远超其他年份,要警惕是不是全市场异常流动带来的;
- 在回归中加入年份虚拟变量与情绪指数的交互项,看极端年份是否有显著不同的关系;
- 稳健性检验中用缩尾后再回归,确认结论不是极端值单独贡献的。
5.3 情绪与收益之间的内生性问题
情绪指数本身来自交易,交易又受收益影响,这就形成了循环关系。比如当天股价大涨,市场情绪升温,换手率放大,后面收益继续受情绪影响。这种双向因果关系会让普通OLS系数有偏。常用的缓解方案:
- 用滞后一期或者期初情绪代替当期情绪;
- 用行业内其他公司的平均情绪作为公司情绪的替代工具变量(注意需要通过相关性检验和排他性约束);
- 用面板GMM估计,把滞后收益和滞后情绪作为工具变量。
我自己的经验是:先用滞后一期情绪做基准,再用行业内均值情绪做IV,如果两者方向一致,就不再纠结内生性。毕竟数据是观测数据,彻底解决内生性很难,但多角度交叉验证至少能让结论令人信服。
5.4 缺失值处理千万别用全样本均值填充
情绪指标缺失往往不是随机缺失。比如一家公司当年长期停牌,换手率缺失,实际情绪状态应该是极低或者极度麻木,用全样本均值填充会彻底抹掉这个信息。我的原则是:缺少换手率或波动率的个股年度观测,除非是用其他指标(如交易量)能推出来,否则直接标记为缺失,在回归中不纳入。如果发现某一年全市场大量缺失,要优先回到原始行情数据自建指标,而不是依赖下游整理好的数据源补数。
6. 数据可视化:让情绪面板的分布规律一眼可见
6.1 年度情绪散布图
拿到面板后,先别急着回归,用一张图看看宏观规律。以年份为横轴,情绪指数为竖轴,画散点并用箱线图覆盖,能快速看见市场情绪高潮年份和低谷年份。2007-2024年间,你会看到某些年份中位数明显上移,同时上下须拉长,这说明分歧加大。这一步能帮你决定是否需要分年份标准化,以及哪些年份要格外留意。
6.2 个股情绪路径图
对少部分你重点关注的股票样本,画情绪指数随时间的折线,再配上收益率的累计曲线。情绪和收益的错位经常看得很直观:情绪达到历史高位后,未来几个月收益容易回落;情绪跌到低位时,若伴随成交量萎缩,后续可能慢慢修复。不过注意,面板数据年份粒度比较粗,这种择时关系只能用于中期参考,更精细的日内或月内情绪需要更高频数据。
6.3 热力图监测截面异常
我会把情绪指数按公司和年份排成行-列矩阵,用热力图展示。如果看到某一行突然有很亮的色块,说明该股当年情绪极度亢奋,大概率伴随股价异动,可以追溯到公告事件。这个操作适合数据清洗阶段的“异常值扫描”,比单纯看数字更敏感。
7. 扩展思考:未来情绪数据还能怎么用
这篇文章最后想聊一点前瞻。传统的交易行为指标虽然覆盖广,但情绪的内涵随着信息传播方式变化,文本情绪、舆情关注度逐渐成为新维度。如果你只有2007-2024年的面板数据,可以把交易情绪指数与公司新闻情感指数结合,构建一个“综合情绪状态”指标。
比如在面板数据中增加两列:媒体报道情感得分均值、互动平台帖子情感值。然后用Word2Vec或者大语言模型对公告、新闻做情感打分。因为文本数据频率高,可以按月生成再年度聚合,正好能补足交易指标在某些平静时期变化不足的问题。
如果自己先从零头开始处理,建议按以下顺序扩展:
- 用现有面板做主成分,保留第一主成分并命名“交易情绪”;
- 另用新闻情感数据做主成分,命名“媒体情绪”;
- 再对两个指数做面板回归,观察媒体情绪能否对交易情绪产生增量预测;
- 进一步把两者交互项放进收益模型,看“交易情绪高涨+媒体情绪也高涨”时是否更容易发生价格反转。
这种扩展最大的好处是物尽其用:原始面板数据作为地基,文本数据作为增量层,整套框架既不过度复杂,又能兼顾稳健性。
8. 最终实操心得:一份可靠情绪面板的自我检查清单
在真正把数据用于论文或策略回测之前,我会照着下面这份清单过一遍,每项都确认了才放心。你可以直接抄走。
- 数据完整度:检查每个ticker-year是否唯一,是否有重复索引。如果有重复,要查是数据合并错误还是来源重复,不能直接drop。
- 指标分布稳定性:画出主要代理指标的时间序列图,寻找异常断点。比如换手率中位数在某一年突然跳变,需排查是否口径变了。
- 行业中性化效果:把中性化前后情绪指数对行业虚拟变量回归,理想情况下中性化后R方应该很低,证明行业信息已被剥离。
- 前视偏差检查:确保所有用到的情绪指标日期都早于被解释变量日期,这一点怎么强调都不为过。
- 分组单调性:分组检验中的均值或中位数若完全非单调,要警惕非线性关系,之后回归里考虑加入平方项。
- 稳健性检验:换用替代情绪定义(比如不用主成分,只用换手率)后,核心变量系数的方向必须保持一致,否则说明变量构造过度依赖。
说句实在话,投资者情绪数据从来不是“装上就能跑”的现成资源,它需要研究者对手中数据的来龙去脉完全透明。2007到2024横跨牛熊,本身就是一座绝佳的情绪演化样本库,只要处理得当,你能从中挖出很多有趣而稳健的规律。我自己在这类数据分析上踩过的最大坑,就是拿到别人整理好的面板后,只看了摘要统计就上手回归,结果被某个特殊年份驱动的假结论带偏。从那以后,我的习惯变成:字段含义逐一确认、构造代码自己复现一遍、最关键的结果用三个月度替代指标交叉验证。这套流程虽然费点时间,但能省去大量后期返工的麻烦。
如果你正准备用这份面板数据去跑回归,建议先复制一份原始数据做备份,再按上面说的清洗和中性化步骤走一遍,用分组检验和滞后收益模型确认数据质量。数据本身不会告诉你结论,但一份高质量的情绪面板能让你离真实结论近一大步。