ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

A股股票简称相似度全解析:数据清洗、算法选型与时代规律

A股股票简称相似度全解析:数据清洗、算法选型与时代规律

1. 从“撞名”说起:为什么要分析股票名称相似度

做投资的人多少都有过这种体验:盯盘时看到两个名字差不多的股票,第一反应是“这俩是不是一家公司”。比如某家叫“XX智能”的公司,和另一家叫“XX智控”的公司,业务领域高度重叠,股价走势也经常同步,但翻开财报才发现,两者完全没有任何股权关系,只是行业相同、命名习惯相似而已。这种“撞名”现象在A股市场不是个例,而是持续了三十多年的普遍存在。

1990年沪深交易所相继开业以来,A股累计出现过几千只股票,每一只都有一个中文简称。这些名称不是随随便便起的,它们既反映了公司所处行业,也映射了每个时代的市场偏好。1990年代遍地是“股份”“实业”,2000年前后“科技”“信息”泛滥,2015年之后“传媒”“互金”扎堆,到了2021年以后,“新能源”“智能”“算力”又成了新宠。名字里的词汇是有年代感的,而不同年代的命名趋同,直接造成了大量相似名称的历史堆积。

我做的这个项目,核心是把1990到2025年之间A股上市公司使用过的所有股票简称抓下来,用中文文本相似度算法做两两比对,找出那些容易混淆的名称组合,再从时间、行业、变迁三个维度去拆解“撞名”现象背后的规律。这个分析框架不复杂,但做下来之后,能发现不少有意思的东西:比如同行业公司名称平均相似度远高于跨行业,比如大量公司更名之后会和另一家公司的老名字撞车,再比如某些年份上市的公司名称相似度明显高于其他年份。

这篇文章不打算只讲结论,我会把从数据获取、清洗、相似度计算到结果验证的完整流程拆开讲,把我踩过的坑也一并交代清楚。如果你也想做类似的中文短文本相似度分析,或者单纯想看看A股三十五年来的命名演变,这篇内容应该能给你一个可以直接参考的完整方案。

2. 数据准备:股票简称的历史完整度是分析的地基

2.1 数据源选择与字段设计

做相似度分析,第一件事是把“某一时期某只股票叫什么名字”这件事查清楚。A股股票简称不是一成不变的,公司可能会因为重组、借壳、主业转型、注册地变更等各种原因改名字。更有意思的是,一只股票在退市整理期、被ST或*ST期间,简称还会携带特殊前缀或后缀。如果不把时间维度纳入数据模型,相似度计算就会把同一只股票的不同时期名称当成多个实体,结果自然失真。

我用的数据源是公开市场行情数据接口,外加各交易所官网的股票简称变更公告做交叉校验。数据字段不需要太复杂,核心就四列:

字段名类型说明
stock_code字符串股票代码,作为唯一实体标识
short_name字符串该时间段内使用的股票简称
start_date日期该简称生效的开始日期
end_date日期该简称失效的结束日期

很多公开数据源只提供最新简称,历史简称要单独抓。这里我建议优先用交易所披露的“证券简称变更记录”或者财经数据平台的“曾用名”字段做基础,再用行情软件的日线快照做补全。为什么?因为“曾用名”字段通常是文本列表,比如“XX股份(曾用名:XX实业、XX控股)”,解析的时候要自己切分,容易漏掉变更日期;而日线快照每一根K线都带当时的简称,按日期去重就能还原出精确的时间线。

2.2 时间线数据的清洗策略

拿到原始数据后,清洗环节有几个隐藏的坑。第一个坑是简称里带空格。交易所的正式简称有些是“XX 科技”这种带空格的,但行情软件里通常会省略空格,如果你用的是两个来源的数据合并,同一时期的简称可能对不齐。我统一把所有空格和全角字符转成半角,再做去重和排序。

第二个坑是简称变更时的“交接日”重复。某只股票在2023年5月10日发布更名公告,新简称从5月11日开始生效,但5月10日当天,日线快照里可能同时出现两种简称,或者新简称已经提前出现在某些数据源的T+1字段里。我的处理方式是:遇到同一代码、同一日期出现两个不同简称,优先采用交易所公告的生效日规则,把旧的截止日期设为新生效日的前一天,避免同一天重叠。

第三个坑是退市和暂停上市期间的简称。这类股票在退市整理期或长期停牌期间,简称会变成“XX退”“XX3”之类的特殊形式。如果你关心的是“投资者日常会看到什么名称”,这类简称也需要保留,但计算相似度时要和正常简称区分处理,避免干扰统计。

提示:建议在数据清洗后做一次全局质检——按股票代码分组,检查每只股票的简称时间线是否连续,有没有出现“上一个简称的结束日期早于下一个简称的开始日期”这种断档。断档意味着数据缺失,要么补充数据源,要么至少记录下来,在最终统计时剔除这只股票。

2.3 数据范围的界定

1990年到2025年跨度达35年,期间A股股票数量从最早的“老八股”发展到现在的数千只,累计出现的股票简称总数超过一万条。如果只算当前存量股票的历史简称,大约在几千条量级;如果把历史上已经退市的股票也全部纳入,总量会明显增长。

我建议不要省略退市股票。因为退市股票的名称恰恰是历史遗留下来的“撞名”高发区——早年上市的很多公司叫“XX股份”,后来陆续退市,但它们当年的简称和现在一些活跃股票的简称仍然高度相似。只做存量股票的话,会低估市场整体的名称混淆度。

数据范围确定后,还需要考虑是否包含B股。B股简称通常带“B”后缀,比如“XXB”,从计算机相似度的角度,加不加后缀结果会差一个字符的编辑距离。我的选择是包含B股,但在代码层面用股票代码的第三位(“900”开头是沪市B股,“200”开头是深市B股)做标记,后面对比时区分“跨A/B股相似度”和“A股内部相似度”两个维度分别统计。

3. 相似度算法的选型:短文本相似度,不能只看字面

3.1 为什么通用文本相似度方案在这里要打折扣

股票简称是一种特殊的短文本,长度集中在2到6个汉字之间,绝大多数是4个字或3个字。这种文本有两个特点:一是没有足够的上下文语境,二是词语顺序直接影响含义。

通用文本相似度方案里,TF-IDF加余弦相似度是最常见的做法。但对两三个字的股票简称,TF-IDF几乎没有区分能力——因为整个“语料库”里每个词出现的频率都很高,IDF权重拉不开差距,而且分词之后只剩一两个词,向量空间接近空转。BERT这类深度学习模型就更不用说了,虽然语义理解能力强,但对这么短的文本,它的优势发挥不出来,反而会引入“语义近义但字面不同”的误判,比如“锂电”和“电池”语义相近,但在我们分析“名称看上去像不像”这个场景里,这两个词不该被当成高相似度。

所以这里的核心度量指标不是“语义相近”,而是“字面相似”。目标很明确:找出那些让普通投资者一眼看上去会认错或混淆的名称对。

3.2 三种实用算法的组合打法

我做下来比较有效的方案,是用三种算法同时计算,然后做交叉筛选:

  • 编辑距离(Levenshtein Distance):把字符串A变成字符串B所需的增、删、改次数。对“XX科技”和“XX科持”这种单个错别字级别的差异,编辑距离能直接反映出来。归一化时除以两个字符串的最大长度,得到“归一化编辑距离”,值越小越相似。
  • 字符n-gram重合度:把字符串切成长度为2或者3的连续字符段。比如“XX智能”的二元组是{XX, X智, 智能},“XX智控”的二元组是{XX, X智, 智控},两者的重合度立刻就能算出来。n-gram的好处是容忍部分字符不一致,适合捕捉“同一行业词+不同尾字”这种常见撞名模式。
  • 最长公共子序列占比:不要求字符连续,按相对顺序找两个字符串共同包含的最长字符序列,除以较短字符串的长度得到占比。这个指标对“AB科技”和“AB智能科技”这种包含关系特别有效——前者的字符几乎全部出现在后者中,容易被编辑距离漏掉。

三种算法算完之后,我通常以归一化编辑距离为主指标,n-gram重合度与最长公共子序列占比为辅助指标,做下面这种分层筛选:

相似度等级判断条件判定含义
高度相似归一化编辑距离 ≤ 0.25 且 n-gram重合度 ≥ 0.6极容易混淆,值得重点审核
中度相似归一化编辑距离 ≤ 0.5 且 n-gram重合度 ≥ 0.4存在混淆可能,需要人工确认
低度相似超出以上范围不需要特别关注

注意:阈值不是拍脑袋定的。我试过几组参数,0.25这个高度相似阈值能覆盖“四个字里改一个字”“三个字里删一个字”这些典型场景;低于0.25的,比如四个字里改两个字,虽然算法分数仍然不低,但实际肉眼看已经不容易搞混,人工复核成本会急剧上升。

3.3 为什么不直接用拼音或读音相似度

股票名称的混淆不只发生在字面层面,很多中文名称读音相近但字形完全不同,比如“兴”和“新”、“德”和“得”、“智”和“置”。理论上应该引入拼音相似度,但实际上股票简称的读音混淆远少于字形混淆,而且一旦引入拼音,会把大量无关名称误判为相似——中文里同音字太常见了,稍微长一点的名称就会出现“读音重叠但看着完全不像”的情况。

我采取的做法是:在最终展示结果里,把读音近似但字形不同的名称对单独归为一类,标注“读音近似”,作为参考信息,不进入核心的“字形相似”统计。这样既承认了“听上去也容易混”的现实,又避免把度量标准搞得太宽松。

4. 实操:从零构建股票名称相似度分析流程

4.1 数据查询与时间线合并

数据准备完毕后,第一步是查询所有历史简称。我用的是Python加Pandas,从数据库里直接拉取全量简称时间线。核心代码框架大致如下:

import pandas as pd # 加载数据 df = pd.read_csv('stock_names.csv', dtype={'stock_code': str}) df['start_date'] = pd.to_datetime(df['start_date']) df['end_date'] = pd.to_datetime(df['end_date']) # 清洗:去空格、统一大小写 df['short_name'] = df['short_name'].str.replace(' ', '').str.upper() # 剔除无效记录 df = df[df['short_name'].notna()] df = df[df['short_name'].str.len().between(2, 8)]

这段处理里有个细节容易被忽略:.str.len()计算的是字符个数,但Python中中文字符占一个长度单位,英文和数字也各占一个,所以"XX科技"的长度是4。如果混入了Unicode的扩展字符,比如生僻字,长度统计可能会异常,建议清洗后打印一下长度分布,确认大部分名称集中在3到5个字。

4.2 相似度计算与配对

接下来就是核心的相似度计算。先实现归一化编辑距离,这里我直接用现成的库函数加速:

import numpy as np from difflib import SequenceMatcher def normalized_edit_distance(s1, s2): from rapidfuzz.distance import Levenshtein max_len = max(len(s1), len(s2)) if max_len == 0: return 0.0 return Levenshtein.distance(s1, s2) / max_len def ngram_overlap(s1, s2, n=2): g1 = {s1[i:i+n] for i in range(len(s1)-n+1)} g2 = {s2[i:i+n] for i in range(len(s2)-n+1)} if not g1 or not g2: return 0.0 intersection = g1 & g2 union = g1 | g2 return len(intersection) / len(union) def lcs_ratio(s1, s2): match = SequenceMatcher(None, s1, s2).find_longest_match(0, len(s1), 0, len(s2)) shorter = min(len(s1), len(s2)) if shorter == 0: return 0.0 return match.size / shorter

计算全部两两配对时要注意复杂度。假设累计简称有N条,两两比对就是N的平方次运算。N在1万条以上时,这个量级是上亿次计算,虽然每个字符串都特别短,纯Python循环仍然可能跑好几个小时。我的做法是先做一层粗筛:用倒排索引按“包含相同二字符子串”做候选集筛选,只有存在共同二元组的名称对才进入精确计算。这一步能把计算量缩减到原来的十分之一甚至更低。

粗筛的逻辑不复杂:把每个名称拆成二元组集合,建立“二元组 -> 名称列表”的倒排索引,然后对每个名称,取它所有二元组对应的名称列表做交集候选。所有候选对再走一次三种相似度算法的精确计算,五分钟左右就能跑完全部数据处理。

4.3 相似对输出与人工复核

相似度计算完成后,我按阈值筛选出所有高相似和中相似的名称对,输出成下面这种表格:

名称A名称B归一化编辑距离n-gram重合度最长公共子序列占比是否属于同一行业
XX智能XX智控0.250.600.75是
XX实业XX实利0.250.500.75否
XX科技XX科技控股0.400.801.00是

输出之后一定要做人工复核。为什么?因为算法只能判断字面相似,判断不了“是否真的会造成混淆”。有些名称对虽然字面高度相似,但一个是家喻户晓的大盘蓝筹,一个是市值很小的创业板公司,普通投资者几乎不会搞混;反过来,有些名称对字面相似度只有中等水平,但业务领域完全重合,市场上确实存在“买错股票”的投诉案例。

复核时我采用了两轮机制:第一轮是快速通读高相似度列表,把所有明显属于同一实体的名称对剔除(比如某只股票更名前后的名称,虽然算法给高相似度,但它们本来就是同一家公司);第二轮是抽样检查中度相似列表,确认是否有漏网的典型混淆对。

4.4 为什么要把“同一实体”单独处理

前面提到更名前后的名称对要剔除,这个环节我给它单独建了一张表。举例来说:某只股票1998年叫“XX实业”,2007年更名“XX控股”,2015年又更名“XX科技”。这三个名称在相似度矩阵里两两之间可能有很高的分数,但它们指向同一个股票代码,不是不同实体之间的混淆。处理方式是在最终统计时增加一个排除条件:如果两个名称对应的股票代码相同,则不计入“不同实体相似对”。

但这里还有一层更隐蔽的情况:借壳上市。A公司被B公司借壳后,壳代码没变,但名称从“A股份”变成了“B科技”。从代码层面看,它们仍然算“同一实体”,但从投资者视角看,市场上确实存在这么一段时间:老名称的新闻还在传播,新名称已经出现在行情列表里,买错的人和看错的人都会出现。我最后的处理是把这类虽然同代码但名称差异巨大的“跨时代名称对”单独归档,不放进主体统计,但记录在案,作为“历史沿革混淆”的专项案例。

5. 跨越35年的命名时代特征

5.1 逐年相似度分布反映市场风格

相似度计算最大的价值,不是找出单个撞名对,而是从整体分布里看出市场风格的年代变化。我把所有名称按首次出现年份分组,计算每个年份新增名称的组内平均两两相似度。结果非常直观地呈现出几个阶段:

  • 1991到1996年:平均相似度高。早期上市公司以“XX股份”“XX实业”为绝对主流,名称同质化严重。
  • 1997到2000年:“科技”开始升温,但“股份”仍然占很大比例,整体相似度回落但不明显。
  • 2001到2008年:名称差异化程度增加,地产、医药、制造等行业词丰富起来,平均相似度明显下降。
  • 2010到2015年:影视、传媒、手游等概念企业扎堆上市,“XX传媒”“XX影业”“XX游戏”形成新的同质化小集群。
  • 2020到2025年:“新能源”“智能”“科技”“数字”等词高频出现,相似度迅速回升。

这个分布很有意思——它说明名称相似度不是静止的,而是跟随产业周期和上市节奏波动的。某一年如果集中上市同一行业的公司,这些公司的名称相似度就会拉高,因为它们倾向于采用同一个“行业热门词”来命名。

5.2 同一个行业词如何在不同代际间迁移

更深入的观察是“行业热门词”的时间迁移。1990年代末最能代表高科技的是“信息”,2005到2010年变成“光电”,2015年左右是“传媒”,2018到2020年是“科技”泛化,而2021年之后“智能”“算力”“数据”开始占据新上市公司命名的高频位置。“XX智能”这个名字,在2021到2025年之间新出现的频率,比之前二十年加起来还多。行业词汇的扎堆效应,导致同一时期上市的公司名称相似度系统性偏高。

还有一个值得注意的现象是旧词复用。2000年曾有一批“XX网络”的公司上市,其中不少后来经营不善退市了,名称沉寂了十几年。最近几年“网络”这个词在科创板的新公司命名中又开始出现,和旧名称形成了跨代际的字面相似。分析相似度矩阵时,这类“老退市-新上市”的名称对经常被挖出来,人工复核时觉得特别恍惚——两个年代相隔二十年的公司,名字却像兄弟。

5.3 更名行为对相似度矩阵的动态影响

股票更名不是小概率事件。历史上A股累计发生过数千次证券简称变更。每一次更名,本质上就是给市场增加了一条新的“名称痕迹”。如果一个新简称和某只存量股票的高度相似,那撞名风险会持续到下一次其中一方更名为止。

我在流程里增加了一个“有效期重叠检测”模块:对每个高相似名称对,检查两个名称各自的有效期是否有重叠区间。只有有效期重叠,投资者才可能在同一天看到两个相似名称;如果时间完全错开,比如甲名称1995年退市,乙名称2020年才出现,那它们虽然字面相似,但现实中不存在混淆场景。

这个重叠检测的结果让我重新调整了筛选标准——最终进入“实际混淆风险名单”的名称对,必须满足字面相似度高且有效期有重叠。这样统计出来的数量比纯字面相似少很多,但每一条都有真实的现实意义。

6. 项目中的常见问题与排查技巧

6.1 高频问题速查表

做这类分析时,最常遇到的问题集中在数据、算法、结果三个层面。我整理了一张速查表,后面细说:

问题现象可能原因排查思路
名称时间线出现断档停牌期间行情数据未更新用交易所公告补全暂停期间简称
两个名称明明不同却算出高相似度其中一个是另一个的包含关系,比如“XX”和“XX控股”结合最长公共子序列占比人工判断
同一只股票不同时期名称互相匹配数据中缺少股票代码维度增加“同一代码不参与实体间对比”逻辑
候选集太大、跑不完两两比对复杂度太高先用共同二元组粗筛,再做精确计算
高相似对过多但人工复核全否阈值设定过宽收紧阈值,增加有效期重叠检测

6.2 三个我亲身踩过的坑

第一个坑是B股后缀问题。我最早跑出来的高相似名称对里,有相当大比例是同一家公司的A股简称和B股简称,比如“XX机电”和“XX机电B”。从技术上它们确实相似,但现实里这些是不该统计的重复实体。解决方式是单独做一层“同母公司A/B股去重”,识别规则是去掉后缀后前缀完全一致且股票代码同属同一家公司。

第二个坑是“名称包含”带来的误判。比如某公司叫“XX科技”,另一家叫“XX科技集团”,编辑距离虽然不低,但普通投资者看到这两个名字时,更容易把它们判断为“名称相关”,而不是“名称混淆”。我后来在输出时增加了一列标注“包含关系”,把这类名称对和“替换关系”区分统计,效果好了很多。

第三个坑是数据源的历史简称缺失。某些行情数据源只提供近五年的历史简称,更早的只显示“曾用名”汇总,导致我早期统计的相似对数量系统性偏低。后来我换了一个数据接口做交叉验证,把缺失的年份补齐,整体相似对数量比第一版多了近三成。吃一堑长一智,现在我的流程里固定包含数据完整度校验环节——对每只股票,统计简称时间线的覆盖天数,覆盖率低于95%的股票单独标记,不让它在结果统计里占据权重。

6.3 一个小技巧:用相似度矩阵做“行业术语流行度”统计

最后分享一个我后来加的扩展功能。既然名称相似度矩阵能反映出同行业命名趋同,那反过来,我可以从矩阵里提取出“哪些行业词在哪些年份流行”。方法不复杂:把所有名称切分成二元组或者三元组,统计每个n-gram在不同年份的出现次数,再除以该年新增公司数做归一化,得到一条“行业词热度曲线”。

比如“科技”这个词作为名称组件的热度,1990年代末有一个陡峭的上升,之后回落,2015年前后又有反弹,2020到2025年再次走高。这条曲线和市场对这些概念的投资热度高度相关。我对比了几个行业词的曲线,发现它们的高峰期往往滞后或者同步于对应行业板块的行情高峰期——公司命名确实存在“追热点”的特征。

这个扩展功能没有改变核心的相似度分析逻辑,但让整个项目从“找出容易混淆的名字”变成了“理解行业命名逻辑的时间演变”。做完之后,我再看那些高相似度的名称对,就不只是把它们当成数据异常,而是能看出背后行业轮动、更名潮和时代审美的痕迹。

根据我个人做数据分析和文本挖掘的经验,股票名称相似度这个项目最难的地方不在算法,而在数据深度和理解深度——你需要把每一个相似名称对放回它的时间坐标和行业坐标里,才能真正判断它有没有意义。如果你也想跑一遍类似的分析,建议从近十年的数据开始,样本量足够大、数据源相对完整、现象也足够明显,等流程跑顺了再回溯1990年代初期的老数据。这样循序渐进,比一上来就铺开全量数据要稳妥得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进