ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

分析师关注度与跟踪度指标构建:CSMAR数据+Stata完整实现

分析师关注度与跟踪度指标构建:CSMAR数据+Stata完整实现 在学术研究尤其是公司金融、资本市场相关的实证论文里分析师关注度Analyst Coverage和跟踪度基本属于“兵家必争之地”的变量。只要你的选题涉及信息披露、股价同步性、盈余管理、创新投入、机构投资者行为几乎都绕不开这个指标。但真正动手去CSMAR或者Wind里拉数据时很多人会卡在第一步到底该用哪张表怎么把一份份研报汇总成公司层面的年度指标用“分析师人数”还是“机构数量”才不会被审稿人Challenge这篇文章我直接基于2001-2024年沪深A股上市公司的分析师关注度、跟踪度数据把构建逻辑和Stata实现代码完整拆开来讲。数据源以CSMAR的中国上市公司分析师预测研究数据库为准代码跑完直接得到面板数据公司-年度一一对应可用于后续回归。1. 数据整体设计与思路拆解1.1 关注度和跟踪度到底是什么关系很多初学者会把“分析师关注度”和“分析师跟踪度”混为一谈其实这两个指标在文献里有明确的分工构建方式也不一样。分析师关注度Analyst Coverage衡量的是在一年之内有多少位分析师对某家上市公司发布过研究报告。它突出的是“人”的维度反映的是卖方研究力量对公司的覆盖密度。通俗讲就是有多少双眼睛在盯着这家公司。分析师跟踪度Analyst Following/Tracking则更强调“行为”的持续性通常用“分析师发布了多少份报告”“有多少家券商机构在跟踪这家公司”来衡量。它突出的是“频次”和“机构”的维度反映的是市场对这家公司的持续关注程度而不只是临时来看一眼。学术研究里比较常见的做法是关注度 该公司当年被多少位分析师出具过报告分析师人数跟踪度 该公司当年被多少家券商机构出具过报告券商机构数或者用报告总份数区别这两者非常关键。因为一位分析师一年可能发4份报告每季度1份也可能4位分析师各发1份报告。如果你用“报告总数”代表关注度这两个场景数值一样但如果你用“分析师人数”前者是1后者是4。不同口径的结果差异可能直接改变实证结论的显著性。1.2 为什么从2001年开始构建2001年是国内卖方研究逐步规范化的起点。2000年之后国内券商研究所开始大规模转型证券投资咨询业务逐步成熟分析师的盈利预测和评级报告数据才具有了系统性的积累。CSMAR的分析师预测数据库也是大致从2001年开始有比较完整的覆盖。所以做2001-2024年的长面板有一个天然的好处完整覆盖了A股市场的多轮牛熊周期、监管政策变迁和信息披露制度的完善过程。无论是做时间维度的趋势分析还是做政策冲击的DID研究起点都足够早。这里有一个实操建议数据下到2001年之后可以顺便统计一下每年的样本量看看覆盖率的演变。这既能作为稳健性检验的样本范围依据也可以作为论文里的描述性统计内容。1.3 构建方案选型的核心逻辑我先说一下整体思路实际操作分四步走第一步从CSMAR下载分析师预测明细文件拉出2001-2024年所有A股上市公司的分析师盈利预测记录。这里要选对表通常用的是“分析师预测明细表”或“盈利预测表”里面包含券商代码、分析师代码、研报发布日期、被预测公司代码等核心字段。第二步对原始数据进行清洗和标准化。剔除B股、H股、已经退市但报告期内仍在交易的公司剔除预测日期和公司代码为空、明显异常的数据。尤其是公司代码CSMAR里有的字段是股票代码例如600519.SH有的是证券代码例如600519统一格式后才能正确merge。第三步按公司代码和年度进行分组聚合。用唯一分析师ID计算人数用唯一券商机构ID计算机构数用记录条数计算总报告数分别生成关注度和跟踪度指标。第四步与公司财务数据、股票交易数据、公司基本信息匹配得到最终的面板数据。这套方案的优点是逻辑清晰、便于扩展而且每一步都有明确的“解释变量编码”依据写进论文的变量定义表里非常好看。缺点是如果你不太熟悉Stata的collapse和merge命令容易出现数据量翻倍或匹配错位的问题。这些都是小问题下面我会把坑一个一个指出来。2. 核心细节解析与实操要点2.1 CSMAR分析师数据表的核心字段在真正动手写代码之前先把要用的字段搞清楚。CSMAR分析师预测研究数据库里的核心表通常包含下表中这些字段。需要说明的是不同年份的CSMAR版本界面略有差异字段名可能不完全一样但核心信息是一致的。字段名称典型字段名CSMAR说明股票代码Stkcd / Symbol6位数字代码不包含交易所后缀公司简称ShortName辅助核对用统计截止日期/季度或年度截止日期分析师代码AnalystCodeCSMAR为每位分析师生成的唯一ID分析师姓名AnalystName注意重名问题券商代码BrokerCode券商机构唯一ID券商名称BrokerName辅助核对研究报告发布日期AnnDate / PublishDate关键时间字段预测年度ForePeriod / FY盈利预测对应的财年下载数据时有一个非常容易犯的错误下载区间选了“预测年度”而不是“发布日期”。比如你想构建2020年的分析师关注度应该筛选的是报告发布日期在2020.1.1-2020.12.31之间的报告而不是筛选ForePeriod2020的报告。因为ForePeriod2020的报告可能在2019年就已经发布了分析师提前预测也可以在2021年发布补充预测。如果你用预测年度来筛选口径就全错了。2.2 公司-年度合并口径的统一另一个容易踩坑的点是公司代码格式。CSMAR的分析师明细表里股票代码通常是6位数字字符串比如“000001”但年报财务数据表里股票代码可能是“000001.SZ”这样的带后缀格式。如果你直接拿这两个字段去merge会全部匹配不上或者匹配上但数量少得可怜。正确做法是先把代码统一。我建议在分析师明细表里把Stkcd转成字符串然后保留前6位在公司基本信息表里用substr截取前6位再两边都用destring转成数值型或者都保持字符串格式总之一定要保证两边“同类型同长度”。另外在处理金融行业公司时很多论文会把银行、保险、证券这类金融业公司剔除。金融类公司的财务报表结构特殊杠杆率和分析师关注度的关系跟非金融公司完全不可比。这个是否剔除要看你的研究主题但至少应该在稳健性检验里跑一遍剔除金融行业后的样本。还有一个细节是ST和退市风险警示的处理。2001年到2024年之间有大量公司被ST、*ST或者退市。这些公司的分析师覆盖行为非常特殊——往往是因为风险事件才被关注有严重的样本选择性偏误。如果你不是专门研究ST公司建议在稳健性检验中剔除这些特殊处理状态的样本。2.3 长面板的时间变量处理得到公司-年度面板后年度变量Year的处理也有一堆讲究。分析师报告发布在12月31日和1月1日可能只是隔了一天却被分到了不同的自然年份。如果学界普遍认可的做法是按自然年度归集那么发布在2020.12.31的报告就应该算作2020年的关注度。但这会牵扯到一个问题部分年报发布在次年的1-4月年报发布窗口前后的分析师报告到底算上一年度还是本年度实际操作中大部分文献采用“日历年度”归集也就是按报告发布日期的自然年份来汇总不做任何调整。这样做的原因是口径清晰、代码简单而且审稿人容易接受。当然也有文献用“公司会计年度”来划分这比较少见。我在做数据时就是按照报告发布日期的自然年份来归集的一步到位不用额外生成月份再判断。如果你后续要做季度层面的分析师跟踪可以在生成月度或季度的中间表后再聚合原理一样。3. 实操过程与核心环节实现3.1 环境准备与初始数据导入在跑代码之前先把环境理顺。我这里用的是Stata 15及以上版本建议用Stata 16因为部分字符串函数和日期函数在旧版本里会有差异。数据文件建议全部放在同一个工作目录下至少要保证分析师明细数据、上市公司基本信息、财务数据在同一个文件夹里。新建一个do文件第一行先把工作路径设置好global root D:/data/analyst_coverage cd $root假设你已经从CSMAR导出分析师预测明细数据文件名为“Analyst_Forecast_Detail.dta”或者xlsx格式用import excel读入。导入后第一步是查看数据结构use Analyst_Forecast_Detail.dta, clear describe list in 1/20这里补充说明一下CSMAR导出的Excel文件有的列名里有空格有的字段是中文名。导入后建议先用rename命令改成统一的英文变量名否则后面写代码会出现各种莫名其妙的“variable not found”。例如rename 证券代码 Stkcd rename 分析师代码 AnalystCode rename 分析师姓名 AnalystName rename 券商代码 BrokerCode rename 券商名称 BrokerName rename 发布日期 PublishDate rename 预测年度 ForePeriod3.2 核心变量的清洗与生成下面是整个代码链条中最核心的部分。我把每个步骤都和实际场景对应起来方便你理解为什么这么写。第一步处理日期变量。PublishDate如果导入后是字符串要先用date()函数转成Stata的日期整数如果是数值型的YYYYMMDD格式也要做相应转换* 如果发布日期是字符串格式 2020-01-15 gen date date(PublishDate, YMD) format date %td * 如果发布日期是数值格式 20200115 * gen date daily(string(PublishDate, %18.0f), YMD)第二步生成统计年度。直接取发布日期的年份这是整个聚合分析的核心“分组键”gen year year(date)第三步清洗公司代码。统一转成6位数数值型变量。这里顺带把代码里容易出现的“隐性空格”问题一并解决——CSMAR导出数据经常在字段前后带空格务必用trim处理* 字符串化并去除空格 tostring Stkcd, replace replace Stkcd trim(Stkcd) * 统一格式为6位如 1 -- 000001 replace Stkcd 00000 Stkcd if length(Stkcd) 1 replace Stkcd 0000 Stkcd if length(Stkcd) 2 replace Stkcd 000 Stkcd if length(Stkcd) 3 replace Stkcd 00 Stkcd if length(Stkcd) 4 replace Stkcd 0 Stkcd if length(Stkcd) 5 destring Stkcd, replace第四步剔除关键字段缺失的样本* 剔除缺失值 drop if missing(Stkcd) drop if missing(AnalystCode) drop if missing(year) * 剔除分析师代码为-1或0等异常占位符 drop if AnalystCode 0这里有一个可能遇到的坑部分CSMAR导出数据里同一个分析师姓名在不同年度对应不同的分析师代码或者同一分析师代码在不同年份对应不同的姓名。代码层面建议直接以分析师代码为唯一标识符去重而不要用姓名。因为姓名重名的概率不小尤其像“王磊”“李强”这种高频姓名很容易把两个人合并成一个人。3.3 去重与指标聚合在聚合之前先做一步去重。为什么需要去重因为CSMAR明细表里有些研报会被重复记录。比如同一份研报同时被收录在“盈利预测”和“投资评级”两张子表里如果在导出时没有按唯一键去重你会发现某分析师同一天对同一家公司发布了多条完全相同的预测记录。去重逻辑以“公司-分析师-券商-发布日期-预测年度”为联合键相同记录只保留一条。sort Stkcd AnalystCode BrokerCode date ForePeriod duplicates drop Stkcd AnalystCode BrokerCode date ForePeriod, force这里不建议把ForePeriod放进键里但加上它更安全因为分析师可能同一天对同一家公司发布了2020年和2021年两个财年的预测这是两条不同的信息保留。接下来构建分析师关注度与跟踪度的核心变量* 分析师关注度当年有多少位分析师发布过报告 bysort Stkcd year: egen analyst_count nunique(AnalystCode) * 分析师跟踪度当年有多少家券商机构发布过报告 bysort Stkcd year: egen broker_count nunique(BrokerCode) * 额外当年发布报告的总份数 bysort Stkcd year: gen report_count _N这里用nunique而不是直接去重后再计数是因为nunique可以在不去重的情况下直接统计唯一值数量。但要小心如果你用的是Stata 14及以下版本nunique可能没有被加载。你可以先运行一下ssc install egenmore来安装扩展egen函数集合。如果不想安装扩展命令也可以用两步法实现同样的效果* 按公司-分析师-年度去重后计数 duplicates drop Stkcd AnalystCode year, force bysort Stkcd year: gen analyst_count2 _N * 按公司-券商-年度去重后计数 preserve duplicates drop Stkcd BrokerCode year, force bysort Stkcd year: gen broker_count2 _N tempfile broker save broker restore merge m:1 Stkcd year using broker这两种方式结果一致。如果数据特别大几十万条以上我会推荐第二种方式因为nunique在大数据量和分组特别多时偶尔会卡顿。3.4 聚合结果还原成面板的关键操作这是很多新手最容易翻车的一步。上面用bysort egen生成的数据每一行仍然保留着原来明细数据的条数——也就是说如果某公司某年有50条研报记录那这个公司年度就会重复出现50行相同的数据。这个数据不能直接用来做面板回归必须先折叠成一个公司-年度只占一行的数据。折叠方式有两种第一种先保存明细数据方便后续做其他分析再collapse成面板数据* 保存明细级别数据 save Analyst_Detail_clean.dta, replace * 折叠成公司-年度面板 collapse (max) analyst_count broker_count report_count, by(Stkcd year) save Analyst_Coverage_Panel.dta, replace这里用(max)是因为在用egen生成变量时同一组内的每条记录都有相同的聚合值取max等价于取那个唯一值不会造成信息损失。理论上用mean、first都行但max最稳妥。第二种用独热占位的方式先collapse再计数。这种方法适合需要在一行内同时统计多个维度如行业分析师数量的场景gen byte one 1 collapse (sum) one, by(Stkcd year) rename one analyze_collect不过这种方式统计的是总报告数不是唯一分析师数需要先处理好唯一性问题再这样用。我推荐用第一种方式简单直接不容易出错。3.5 与公司基本信息及财务数据的匹配有了关注度面板数据后最关键的一步是merge到你的公司财务面板上。这里建议用每月末或年末的上市公司基本信息文件包含Stkcd、year、行业代码、上市状态、总资产、营业收入等。一个常见的问题是分析师明细数据里出现的公司有的在2024年已经退市有的公司代码发生了变更比如吸收合并后代码作废。如果直接用Stkcd做merge退市公司会被自动剔除但这不影响你论文的主回归因为主回归本来就需要有财务数据才能跑。匹配代码use 公司财务数据.dta, clear merge 1:1 Stkcd year using Analyst_Coverage_Panel.dta, keep(1 3) nogen * 将缺失的关注度填充为0没有分析师覆盖0 replace analyst_count 0 if missing(analyst_count) replace broker_count 0 if missing(broker_count) replace report_count 0 if missing(report_count)注意这里的填充逻辑。一家公司在某一年没有任何分析师发布报告分析师关注度当然是0。但这里要注意一个问题如果你的样本包含了尚未上市的公司比如IPO前的一年这些公司在上市前不可能有分析师覆盖填充为0虽然技术上不报错但逻辑上不成立。建议在merge前先根据上市日期剔除上市前的年份只保留公司上市当年的年份以及之后年份的样本。生成上市状态变量并做筛选* 假设已有上市日期变量 ListDate gen double listdate_num date(ListDate, YMD) gen list_year year(listdate_num) * 剔除上市前的样本 drop if year list_year这一步做完你的分析师关注度面板基本上就干净了。3.6 缩尾与描述性统计把数据清洗完先别急着跑回归。先看看变量的基本分布确认没有极端异常值。最常用的做法是对连续变量进行1%和99%分位的缩尾处理Winsorize。对于分析师关注度这种计数变量学术界有争议。有的认为不应该缩尾因为“被最多分析师覆盖”本身就是一种真实的市场现象不是错误数据有的则认为缩尾可以让结果不受极值影响。我的做法是主回归用原始值稳健性检验里用缩尾后的值。两种都汇报审稿人也挑不出毛病。缩尾代码* 安装winsor2如果没有 * ssc install winsor2 winsor2 analyst_count broker_count report_count, replace cuts(1 99)但这里要注意winsor2处理整数型变量时会把变量变成浮点数比如把3变成3.0000002。对于计数变量建议先替换成缩尾后的整数gen analyst_count_w round(analyst_count)如果你不想缩尾计数变量也可以选择对分析师关注度加1再取对数这也是一种常见的处理方式尤其适合作为被解释变量时的偏态分布修正gen ln_analyst ln(analyst_count 1) gen ln_broker ln(broker_count 1)4. Stata完整代码汇总与注释到这里我把完整的可运行代码整合成一份可以直接“抄作业”的版本。假设你已经从CSMAR下载了分析师预测明细表导出文件名为“分析师预测_明细.xlsx”且公司基本信息表文件名为“公司基本信息.dta”财务数据文件名为“公司财务.dta”。******************************************************************************** * 分析师关注度、跟踪度数据构建 * 样本区间2001-2024年A股上市公司 * 数据来源CSMAR 分析师预测研究数据库 ******************************************************************************** clear all set more off global root D:/data/analyst_coverage cd $root * 1. 导入原始数据 import excel 分析师预测_明细.xlsx, firstrow clear * 2. 变量重命名根据实际列名调整 rename 证券代码 Stkcd rename 分析师代码 AnalystCode rename 分析师姓名 AnalystName rename 券商代码 BrokerCode rename 券商名称 BrokerName rename 统计截止日期 EndDate rename 发布日期 PublishDate rename 预测年度 ForePeriod * 3. 日期处理 gen date date(PublishDate, YMD) format date %td gen year year(date) * 4. 股票代码清洗 tostring Stkcd, replace replace Stkcd trim(Stkcd) replace Stkcd 00000 Stkcd if length(Stkcd) 1 replace Stkcd 0000 Stkcd if length(Stkcd) 2 replace Stkcd 000 Stkcd if length(Stkcd) 3 replace Stkcd 00 Stkcd if length(Stkcd) 4 replace Stkcd 0 Stkcd if length(Stkcd) 5 destring Stkcd, replace * 5. 剔除缺失值 drop if missing(Stkcd) | missing(AnalystCode) | missing(year) drop if AnalystCode 0 | BrokerCode 0 * 6. 去重公司-分析师-券商-日期-预测年度 sort Stkcd AnalystCode BrokerCode date ForePeriod duplicates drop Stkcd AnalystCode BrokerCode date ForePeriod, force * 7. 生成关注度、跟踪度指标 bysort Stkcd year: egen analyst_count nunique(AnalystCode) bysort Stkcd year: egen broker_count nunique(BrokerCode) bysort Stkcd year: gen report_count _N * 8. 保存明细数据 save Analyst_Detail_clean.dta, replace * 9. 折叠成公司-年度面板 collapse (max) analyst_count broker_count report_count, by(Stkcd year) save Analyst_Coverage_Panel.dta, replace * 10. 与公司基本信息匹配 use 公司基本信息.dta, clear merge 1:1 Stkcd year using Analyst_Coverage_Panel.dta, keep(1 3) nogen * 11. 缺失填充为0 replace analyst_count 0 if missing(analyst_count) replace broker_count 0 if missing(broker_count) replace report_count 0 if missing(report_count) * 12. 生成对数形式 gen ln_analyst ln(analyst_count 1) gen ln_broker ln(broker_count 1) * 13. 如果是合并财务数据后还需要剔除上市前年份 * 这里假定公司基本信息表里有上市日期变量ListDate * gen double listdate_num date(ListDate, YMD) * format listdate_num %td * gen list_year year(listdate_num) * drop if year list_year save Analyst_Coverage_Final.dta, replace * 14. 描述性统计 sum analyst_count broker_count report_count ln_analyst ln_broker4.1 代码执行过程中的速度优化技巧2001-2024年的分析师明细数据全量大概有几百万条到上千万条。如果你的电脑配置一般跑上面的bysort和collapse可能会有点卡。几个加速经验第一个是尽量把不需要的变量在清洗早期就drop掉。分析师明细表里通常有很多冗余字段比如预测EPS、预测净利润、评级调整等。如果你只用到上述核心字段强烈建议在变量重命名之后、数据处理之前保留核心字段其余删除。这样后续排序和聚合会快很多keep Stkcd AnalystCode AnalystName BrokerCode BrokerName PublishDate ForePeriod第二个是用compress压缩数据存储空间。在清洗完成后跑一句compress数据集体积能减小30%-50%后续操作速度会有明显提升。第三个是如果数据真的特别大可以按年份拆分处理最后再append合并。分析师明细基本不存在跨年份的依赖关系所以可以放心拆分。4.2 常见报错及其解决方案这里整理一下我在实际运行中遇到的几个典型报错附带具体解决方案。“nunique not found”或者“command egen is unrecognized”。这是没有安装egenmore扩展包导致的。在Stata命令窗口执行ssc install egenmore然后重启Stata再跑。“variable Stkcd already defined”或者“type mismatch”。这通常是重复执行了同一段代码。建议do文件里全程保持干净的工作环境开头加上clear all。遇到类型不匹配时用describe查看变量存储类型如果是str类型要用destring转换。“merge 1:1 using ...”报错提示“not unique”。这说明公司财务数据或分析师面板数据里存在重复的公司-年度记录。先用duplicates report Stkcd year检查哪边有重复再用duplicates drop Stkcd year, force处理。如果财务数据里同一家公司同一年有两条以上记录例如增发、重组导致多条记录需要先根据实际情况保留一条比如保留年末最后一条记录。“date(PublishDate, YMD)”报错说函数找不到。需要确认变量PublishDate确实是字符串类型如果它是数值型的YYYYMMDD可以用tostring PublishDate, replace转成字符串再用date函数或者直接用daily(string(PublishDate, %18.0f), YMD)。时间变量处理后的日期显示成“01jan2020 08:32:12”这种带时分秒的显著不正常。这说明数据里混入了时间戳格式。可以用split PublishDate, p( )把日期和时间拆开再取第一部分。5. 常见问题与排查技巧实录5.1 缺失值过多问题的定位方法如果你在merge之后发现分析师关注度缺失值特别多比如超过50%先别急着填充0要查清楚缺失到底来自哪里。通常有三种情况。第一种是分析师数据没有覆盖到某些小公司。这属于正常现象小市值、流动性差的股票本来就没什么分析师跟踪缺失值恰恰是这部分公司“低关注度”的体现。填充为0没有问题。第二种是公司代码匹配失败导致的系统缺失。这种情况可以通过分析merge后未匹配样本的特征来识别。比如把所有匹配不上的公司代码单独导出跟公司基本信息表里的代码做对比看看是不是出现代码位数不一致或代码前缀的问题。第三种是时间窗口不对。比如分析师明细表里只有2001-2018年的数据但你merge的财务数据到2024年2019年之后的年份自然全是缺失。这种情况需要回到CSMAR重新下载完整区间的数据而不是简单填充0。判断方法很简单把merge后的数据按年份分年统计analyst_count的缺失比例。如果缺失比例在某一年突然跳变到100%时间窗口和数据源的问题基本实锤。如果缺失比例逐年平滑上升或下降大概率是公司覆盖度的真实变化。5.2 极端值和离群值处理的是与非分析师关注度数据的分布极度右偏头部公司可能一年有50个分析师跟踪尾部公司常年是0。这会给回归带来两个问题。第一如果分析师关注度作为被解释变量用OLS回归时残差可能严重偏离正态假设。这时候建议用ln(1分析师关注度)做被解释变量或者改用计数模型Poisson/Negative Binomial。学术界对分析师关注度作为被解释变量时的模型选择没有统一结论但至少应该在稳健性检验中换一个模型验证。第二如果分析师关注度作为解释变量极值会导致估计系数被少数样本主导。建议在主回归中使用取对数后的值或者缩尾后的值。我自己的经验是ln(1analyst_count)是最平衡的选择既保留了0值又压缩了右尾的极值影响。还有一个容易被忽视的问题分析师关注度在不同年份的均值波动很大。牛市年份如2007年、2015年、2021年整体关注度会系统性上升熊市年份如2005年、2018年则会整体下降。在做面板回归时务必在模型中加入年度固定效应吸收掉这种共同的时间冲击。5.3 分析师人数与券商机构数的选择逻辑最后聊一下实证论文里的变量选择问题。你可能会在文献里看到有人用分析师人数有人用券商机构数还有人用报告份数。这几个指标的差异不是简单的“用哪个都行”。如果你研究的是“信息环境”相关话题如股价同步性、信息披露质量、盈余反应系数分析师人数是更常用的指标。因为每位分析师都是一条独立的信息挖掘渠道人数多意味着市场上有更多独立的信息源。如果你研究的是“卖方竞争”或“利益冲突”比如IPO定价、分析师乐观偏差券商机构数可能更合适。因为同一家券商内部的分析师往往受制于投行部门的利益关系不同券商之间的独立性更强。如果你研究的是“市场关注热度”比如盈余公告后的漂移、投资者关注度报告总份数更能体现这只股票在市场上的“曝光度”。我建议你在主回归中先采用分析师人数口径稳健性检验中换用券商机构数口径这样既符合主流文献又能证明你的结论不依赖特定的变量度量方式。6. 面板回归中的进一步应用数据构建完成之后给你附上一段常用的回归模板方便直接查看数据是否可用。假设你想研究分析师关注度对股价同步性的影响或者反过来研究公司信息透明度对分析师关注度的影响核心回归框架如下use Analyst_Coverage_Final.dta, clear * 合并其他控制变量公司规模、账面市值比、杠杆率、盈利能力等 merge 1:1 Stkcd year using 公司财务数据.dta, keep(1 3) nogen * 变量构造 gen log_size ln(total_asset) gen bm book_value / market_value gen leverage total_debt / total_asset gen roa net_income / total_asset * 剔除缺失值 drop if missing(log_size) | missing(bm) | missing(leverage) * 缩尾 winsor2 log_size bm leverage roa, replace cuts(1 99) * 固定效应回归 xtset Stkcd year xtreg Y ln_analyst log_size bm leverage roa i.year, fe robust est store m1 * 如果是被解释变量是关注度可以考虑计数模型 xtnbreg analyst_count log_size bm leverage roa i.year, fe est store m2 * 输出回归结果 esttab m1 m2 using regression_result.rtf, replace /// b(3) t(3) star(* 0.10 ** 0.05 *** 0.01) /// nogap compress我这里把Y留成了占位符实际使用时替换成你的被解释变量。跑回归之前先做一个变量相关性分析pwcorr ln_analyst log_size bm leverage roa, sig如果ln_analyst和控制变量之间的相关性太高比如超过0.8就要怀疑是不是存在多重共线性问题。实际经验是公司规模log_size与分析师关注度的相关性通常在0.5-0.7之间属于正常范围因为大公司天然吸引更多分析师。但如果超过0.8你可能需要考虑把关注度变量正交化或者用券商机构数替代。6.1 利用分组数据做机制检验分析师关注度数据一个方便之处是天然带有分组维度。比如你可以在券商维度上区分“本土券商”和“合资券商”在分析师维度上区分“明星分析师”和“普通分析师”在报告维度上区分“首次覆盖报告”和“持续跟踪报告”。这些分组可以用来做机制检验。举个例子如果你想验证“分析师关注度通过降低信息不对称程度来提升股票流动性”那么一个自然的机制检验是把分析师关注度拆成“新增分析师人数”和“持续跟踪分析师人数”。新增分析师代表新进入的信息挖掘者持续跟踪分析师代表存量的信息维护者。前者对信息不对称的边际影响通常更大。代码层面构建这两个变量的逻辑是* 标记上年是否已有分析师覆盖 bysort Stkcd AnalystCode: gen first_year year[_N] // 这里逻辑稍复杂需要分别标记 * 更简单的做法用t-1期的覆盖状态来判断 sort Stkcd year by Stkcd: gen lag_analyst analyst_count[_n-1] if year year[_n-1] 1 gen new_analyst max(0, analyst_count - lag_analyst) gen incumbent_analyst min(analyst_count, lag_analyst)这种方法不算特别精确因为不知道具体是哪几位分析师在新增但用于机制检验已经足够了。6.2 数据质量交叉验证的几条经验数据下载完不要直接当作“标准答案”使用。我给你几个简易的验证方法。用Wind或者东方财富Choice查一家知名公司比如贵州茅台、宁德时代在某一年的分析师覆盖数和用CSMAR明细数据汇总出来的数字对比。正常情况下两者应该比较接近差一两个属于合理范围可能因报告统计口径略有差异。如果差10个以上说明你的处理逻辑有问题。分年度看一下全市场的分析师覆盖均值。2001年A股全市场覆盖均值可能在5-8人/家到2015年前后上升到10-15人/家近几年可能有所下降。如果某个中间年份出现大幅度跳变顺手检查一下是不是CSMAR数据库在某个年份变更了覆盖规则。交叉验证时的样本量对比也很有用。2001年A股上市公司大约1100家如果某一年你合并后的公司-年度样本量超过了同年A股上市公司总数的两倍基本可以断定Stkcd或者year变量处理出了问题产生了一对多匹配。6.3 关于未来扩展的一点想法这个数据构建流程本身是一个基础模板。如果你想扩展有两条路特别值得走。一条是分析师层面的网络分析。比如用券商分析师过去共同覆盖的公司网络构造“分析师社交网络中心度”指标。这种数据在近年来顶刊论文里时常见到用来研究信息在网络中的传播。构建思路并不复杂本质上是从“公司-分析师”二部图出发把分析师视为节点以共同覆盖的公司作为连接边计算中心度指标。Stata里可以用ssc install netplot或者导出到Python的networkx里处理。另一条是将关注度数据与文本分析结合。如果你能拿到研报的文本内容可以进一步做“分析师报告语调”tone的分析。关注度解决的是“有没有人看”的问题语调解决的是“看了之后说什么”的问题两者结合就是完整的信息中介刻画。工具层面Stata在文本处理上效率不高建议公司-年度面板继续留在Stata里文本特征用Python跑完再merge回来。写在最后的小提醒这套代码和思路我前前后后跑过好几轮帮朋友救急也不止一次。每次出问题九成以上都出在数据清洗前的准备工作不充分字段名没搞清楚、股票代码格式不统一、发布日期和预测年度没有区分清楚。如果你能把这三件事在动代码之前先想明白后面基本一路畅通。特别建议你把每一步生成的中间数据都单独保存成一个文件比如清洗后的明细数据、折叠后的面板数据、合并财务数据后的最终数据。这样当回归结果不对劲时可以一层一层往前查不用从头开始重跑全流程。做实证研究数据链路清晰比代码跑得快重要得多。
返回列表