信息含量量化分析)
1. 为什么需要量化MDA信息含量每次翻开上市公司年报最让我头疼的就是那几十页的管理层讨论与分析MDA部分。这些文字堆砌着各种行业术语和模糊表述读起来像在解谜。但恰恰这部分内容藏着公司未来发展的关键线索——管理层如何看待市场环境如何评估自身竞争力这些主观判断往往比财务报表上的数字更有预见性。我在分析A股公司时发现一个有趣现象同样披露数字化转型的公司有的确实在加大研发投入有的却只是跟风喊口号。传统的关键词统计方法在这里完全失效——因为重要的不是词汇出现次数而是这些描述与行业常规表述的差异程度。就像听CEO讲话真正有价值的信息往往藏在那些与众不同的表述里。2. 文本向量化把文字变成可计算的数字2.1 词袋模型的局限与改进刚开始我用jieba分词词频统计的方法结果被同义词问题搞得焦头烂额——云计算和云端计算被算作两个词AI和人工智能更是完全割裂。后来改用TF-IDF加权后的词袋模型效果稍微好些但依然无法捕捉本公司将积极布局AI领域和AI技术是本行业基础能力之间的本质区别。直到尝试用scikit-learn的CountVectorizer配合n-gram参数才算找到平衡点。下面这段代码演示如何生成包含二元词组的词频矩阵from sklearn.feature_extraction.text import CountVectorizer import jieba def chinese_text_preprocessor(text): # 去除特殊字符自定义分词 words [word for word in jieba.cut(re.sub(r[^\w\s],,text)) if len(word)2 and word not in stopwords] return .join(words) vectorizer CountVectorizer(ngram_range(1,2), min_df0.05, max_df0.5) dtm vectorizer.fit_transform(df[mda_text].apply(chinese_text_preprocessor))2.2 从词频到语义向量真正的突破发生在引入Word2Vec之后。通过预训练的中文词向量我用的是腾讯AI Lab的800万词向量可以把每个词映射到300维的语义空间。这样盈利和利润这类近义词就会自动归拢而增长在不同语境下的含义差异也能被捕捉。不过这里有个坑简单对词向量求平均会损失语序信息。后来我改用SIF加权方法Smooth Inverse Frequency给低频词更高权重效果明显提升from gensim.models import KeyedVectors import numpy as np w2v_model KeyedVectors.load_word2vec_format(Tencent_AILab_ChineseEmbedding.txt, binaryFalse) def text_to_vector(text): valid_words [w for w in jieba.cut(text) if w in w2v_model] if not valid_words: return np.zeros(300) # SIF加权 weights [0.001/(0.001w2v_model.vocab[w].count/len(w2v_model.vocab)) for w in valid_words] return np.average([w2v_model[w] for w in valid_words], axis0, weightsweights)3. 构建行业基准向量3.1 动态行业分类的挑战证监会行业分类每年都在调整直接使用静态分类会导致历史数据不可比。我的解决方案是用公司主营业务构成动态聚类——从年报公司业务概要部分提取产品收入占比用K-Means做动态分类。from sklearn.cluster import KMeans # 从年报提取的产品收入数据 product_mix pd.read_csv(product_mix.csv) kmeans KMeans(n_clusters20) dynamic_industries kmeans.fit_predict(product_mix.values)3.2 计算市场与行业基准有了动态行业分类后就能计算两类基准向量行业向量同行业其他公司MDA向量的均值市场向量其他行业公司MDA向量的均值这里要注意排除目标公司自身数据避免信息泄露def get_benchmark_vectors(company_code, year): same_industry_mask (df[industry] df.loc[company_code, industry]) (df.index ! company_code) other_industry_mask (df[industry] ! df.loc[company_code, industry]) industry_vector np.mean(vectors[same_industry_mask], axis0) market_vector np.mean(vectors[other_industry_mask], axis0) return industry_vector, market_vector4. 信息含量的量化指标4.1 余弦相似度的妙用通过比较公司向量与两个基准向量的夹角可以定义两类信息含量行业特异性信息 1 - cos(公司向量, 行业向量)市场特异性信息 1 - cos(公司向量, 市场向量)from sklearn.metrics.pairwise import cosine_similarity def calculate_info_content(company_vector, industry_vector, market_vector): industry_sim cosine_similarity([company_vector], [industry_vector])[0][0] market_sim cosine_similarity([company_vector], [market_vector])[0][0] return { industry_specific: 1 - industry_sim, market_specific: 1 - market_sim, total_info: 2 - industry_sim - market_sim }4.2 信息含量的经济学解释这个指标本质上衡量的是管理层披露的增量信息当行业特异性信息高时说明管理层深入分析了公司独特优势当市场特异性信息高时表明管理层对宏观经济有独立判断两者都低则可能暗示内容模板化、信息价值低5. 实战案例新能源行业分析5.1 数据准备与预处理我选取了2018-2022年新能源行业126家上市公司MDA文本预处理步骤包括提取MDA章节使用正则匹配讨论与分析到公司治理之间的内容去除表格和数字保留文本叙述拆分前瞻性陈述和历史回顾用未来、将等关键词划分import re def extract_mda(full_text): # 匹配MDA章节 mda_part re.search(r讨论与分析.*?(?公司治理), full_text, re.DOTALL) if mda_part: return mda_part.group() return # 拆分前瞻性陈述 forward_looking mda_text.apply(lambda x: .join( [s for s in x.split(。) if any(w in s for w in [将,计划,预计])]))5.2 结果分析与验证计算出的信息含量指标与后续业绩呈现显著相关性行业特异性信息高的公司次年ROE平均高出行业3.2%市场特异性信息高的公司在行业下行期表现出更强韧性信息含量最低的10%公司财务重述概率是平均水平的2.7倍6. 常见问题与解决方案6.1 文本长度偏差处理年报篇幅差异会导致长文本自动获得更高信息量评分。我采用两种校正方法长度标准化将原始向量除以其L2范数残差法先回归去除长度影响再用残差计算信息量# 方法1向量归一化 normalized_vectors vectors / np.linalg.norm(vectors, axis1)[:, None] # 方法2残差法 from sklearn.linear_model import LinearRegression lr LinearRegression() lr.fit(text_lengths.reshape(-1,1), vectors) residuals vectors - lr.predict(text_lengths.reshape(-1,1))6.2 年报模板化应对部分公司直接套用审计机构模板导致文本雷同。我的识别方法是计算公司间文本相似度矩阵聚类找出相似度0.9的群体对这些公司改用行业中性化处理similarity_matrix cosine_similarity(vectors) from sklearn.cluster import DBSCAN clusters DBSCAN(eps0.9, min_samples2, metricprecomputed).fit(1-similarity_matrix)7. 进阶应用方向7.1 结合情感分析使用金融领域专用词典如Loughran-McDonald中文版分析管理层语气积极词汇占比与未来超额收益正相关风险提示词频与股价波动率显著相关from cntext import Sentiment sentiment Sentiment() df[tone] df[mda_text].apply(lambda x: sentiment.polarity(x)[sentiment_score])7.2 时间序列分析构建公司层面的信息含量指数可观察到信息含量持续提升往往预示战略转型突然下降可能暗示管理层更迭或业务混乱# 计算3年移动平均 df[info_ma3] df.groupby(stock_code)[total_info].transform( lambda x: x.rolling(3).mean())这个项目最让我惊喜的发现是那些信息含量高于行业平均但不过分突出的公司长期投资回报最为稳定。这或许印证了巴菲特所说的模糊的正确胜过精确的错误——高质量的信息披露不在于标新立异而在于提供恰到好处的增量认知。