做了六七年公司金融实证,并购主题是我自己写论文和帮学生改论文时接触最多的方向之一。这套 2005-2024 年上市公司并购数据加配套 Stata 代码,本质上不是某个数据库的一次性导出,而是从公告数据一路清洗、匹配到回归样本的完整工程。你可以把它理解为一份“已经把坑踩平了”的并购研究数据集:拿到手就能直接跑事件研究、面板回归、异质性分析,不用再从零开始折腾 CSMAR 导出字段、日期格式、去重逻辑这些琐碎事情。
它适合谁?首先是有实证论文需求的经济学、管理学、金融学硕士博士;其次是券商或咨询公司里做并购重组研究、需要快速验证绩效影响的研究人员;还有一部分对并购市场感兴趣的资深投资者,想用数据验证“并购到底有没有创造价值”这类问题。这篇文章会把数据口径、变量定义、清洗流程、回归代码和踩过的报错全部交代清楚。下面按我实际干活的路子来。
1. 项目整体设计:2005-2024 年并购数据到底能做什么
1.1 为什么把区间定在 2005-2024
先说区间选择。2005 年股权分置改革启动,2006 年后上市公司并购重组才进入真正的市场化阶段。如果往前推到 2000 年左右,样本里会混入大量非市场化的资产置换、壳资源交易,研究结论容易被极端案例带偏。往后到 2024 年,刚好能覆盖近 20 年完整周期,包括 2013-2016 年的并购牛市、2018 年前后的商誉爆雷潮、2020 年之后注册制全面推开等几个典型阶段。跨度够大,样本量够多,做分阶段、分行业异质性分析时非常好用。
另外,从 Stata 数据处理角度看,2005 年是一个比较稳妥的起点。早期上市公司数量少、财务数据披露字段不全,尤其 2002 年之前不少公司连季度财务数据都是断档的。2005 年之后财务报表结构基本统一,CSMAR、Wind 里的字段覆盖度也明显变好,做面板回归时不容易因为缺失值把样本削掉一大半。所以如果别人问我“为什么不是 1990 年”,我的回答是:能跑通的数据才是好数据。
1.2 并购数据能做什么研究
这套数据最核心的用处是回答“并购到底有没有创造价值”以及“什么因素影响并购价值”。具体可以拆成几类研究场景。
第一类是事件研究。以并购公告日为事件日,计算累计超额收益率 CAR 和买入持有超额收益 BHAR,检验市场对并购事件的态度。这类研究很常见,但细节极多:事件日选首次公告日还是股东大会决议日,事件窗口选 [-1,1]、[-2,2] 还是 [-5,5],估计窗选多长,都会直接影响结果。
第二类是会计业绩研究。用 ROA、ROE 的变化量衡量并购前后的经营绩效,加入支付方式、关联交易、是否跨界等变量做面板回归。这套数据里我已经把交易金额、支付方式、是否关联、是否跨界等字段整理好,可以直接生成变量。
第三类是政策评估类,比如融资融券试点、注册制改革、产业政策对并购行为的影响。这类研究通常需要把并购数据和其他政策变量合并起来,做多期 DID 或者连续 DID。数据区间从 2005 到 2024 恰好把这些政策窗口都覆盖了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据来源、样本筛选与核心变量定义
2.1 原始数据从哪里来
先说清楚一件事:不存在一个按钮能直接导出“2005-2024年上市公司并购数据”那么干净的文件。市面上的主要来源是 CSMAR 的中国上市公司并购重组研究数据库、Wind 的并购事件库、CNRDS 的中国并购重组数据库。我用的是 CSMAR 为主、Wind 做交叉验证,因为 CSMAR 的字段结构和学术论文接轨度高,但细节字段经常有缺失,需要拿 Wind 和上交所/深交所公告原文补。
拿到底层数据后,我通常保留这些字段:首次公告日期、交易标的、交易买方、交易卖方、交易金额、支付方式、交易状态(进行中/完成/失败)、是否关联交易、交易地位(买方/卖方/标的方)、标的资产类型、上市公司角色。这些字段是做并购研究的地基。如果是从网络爬公告,还要额外解析交易对价、转让比例、业绩承诺这些半结构化信息,工作量会大很多。
2.2 样本筛选的关键口径
筛选口径直接决定样本量和研究结论。我采用的是一套兼顾学术惯例和实际可行性的标准,具体如下:
- 保留买方为上市公司的并购事件,剔除上市公司作为标的方被收购的样本;
- 剔除金融类上市公司,因为银行、券商的财务指标结构特殊;
- 剔除 ST、*ST、退市整理期公司,避免极端财务状况干扰;
- 剔除交易金额缺失或为 0 的样本;
- 同一事件多次公告,按首次公告日保留一条记录;
- 如果一家公司一天内公告多个不相关并购,保留金额最大的那笔,避免事件窗口重叠。
这套标准在 CSMAR 原始数据上跑下来,通常能从 5 万条左右的公告明细筛选出 1 万多条有效事件样本,再和财务数据匹配后,进入回归的样本量根据控制变量要求大约在 6000 到 10000 条之间。需要提醒的是:如果研究问题的焦点是“交易失败的影响”,那么必须把交易状态为失败的样本保留下来,不要一上来就只筛 completed。
2.3 核心变量定义与生成逻辑
这一节很重要,因为变量定义不统一是并购研究里最大的坑。
解释变量方面,常用的是:支付方式(现金支付取 1,非现金取 0;或者分成现金、股份、混合三类)、关联交易虚拟变量、是否跨界并购(行业代码变化)、交易规模(交易金额/公司总资产或取对数)、是否构成重大资产重组。这里要注意,支付方式在不同数据库里字段含义可能不一样,一定要看手册确认“纯现金”是否包含分期支付和现金+承接债务。
结果变量方面,短期看 CAR,长期看 BHAR 和会计业绩变化。CAR 的计算后面会专门讲。会计业绩变量常用并购完成后一年 ROA 与并购前一年 ROA 的差值,或者并购后三年平均 ROA 与并购前三年平均 ROA 的差值。用变化量而不是水平值,是因为能缓解公司固定效应带来的遗漏变量问题。
控制变量基本是公司财务老三样加公司治理:公司规模 ln(总资产)、资产负债率、营业收入增长率、上市年龄、第一大股东持股比例、独立董事比例、账面市值比。行业和年份固定效应必须加。如果做跨行业并购研究,还要控制主并公司和标的公司是否同行业。
3. Stata 清洗与匹配实操:从公告到可回归样本
3.1 环境准备与外部命令安装
这个项目一开始就涉及不少外部命令,最先要解决的是命令安装问题。我强烈建议在跑任何代码之前,先把下面这些装齐:
stata复制ssc install ftools, replace
ssc install reghdfe, replace
ssc install winsor2, replace
ssc install estout, replace
ssc install coefplot, replace
ssc install bdiff, replace
国内用户第一次跑 ssc install 偶尔会遇到连不上服务器或者下载超时的问题。我的经验是给 Stata 配置可用的镜像地址,或者直接从已下载的 ado 文件安装。很多初学者卡在 ftools 上,其实只要注意:Stata 版本尽量新一点,旧版本编译出来的 ftools 和新版本 Stata 内核不兼容是常态。装完可以用 which reghdfe 检查是否调用成功,看到路径信息就说明没问题。
提示:SSC 安装失败时,不要反复重试,先检查
adoupdate能否正常连接。如果始终失败,先尝试手动安装依赖包,再装 reghdfe。
3.2 并购事件样本初步清洗
拿到原始数据第一件事不是跑回归,而是把数据收拾利索。我习惯先把所有变量名改成英文、小写,统一日期格式,然后做去重和缺失值检查。下面这段代码是我每次拿到新批次数据都会跑一遍的框架:
stata复制* 导入原始并购事件数据,假设变量为证券代码、首次公告日、交易金额、支付方式等
use 并购事件原始数据.dta, clear
* 剔除完全没有证券代码的记录
drop if missing(证券代码)
* 日期变量从字符串转换为 Stata 日期
gen date0 = date(首次公告日, "YMD")
format date0 %td
label variable date0 "首次公告日"
* 保留 2005-2024 区间
keep if year(date0) >= 2005 & year(date0) <= 2024
* 删除重复事件:同一公司同一天公告同一标的
duplicates drop 证券代码 date0 交易标的, force
* 交易金额格式整理
destring 交易金额, replace force
replace 交易金额 = 0 if missing(交易金额)
* 生成交易规模变量
gen ln_deal = ln(交易金额 + 1)
* 保存中间文件
save 并购清洗_中间文件.dta, replace
为什么日期变量要单独转换?因为 CSMAR 导出的日期很多是字符串,直接拿去排序或取年份会出错。用 date() 转换后,Stata 才能正确识别时间先后,后面合并股价数据时才能按日期精确匹配。另外,duplicates 去重这一步极其重要,CSMAR 里同一笔并购会因为交易进展更新而出现多条公告记录,比如“董事会预案”和“实施完成”各一条,变量都一样,跑回归时重复样本会把标准误压低,结果虚好看。
3.3 与财务数据、股价数据的匹配
并购事件样本整理好后,就要和上市公司财务数据合并。这一步容易出错的地方是匹配键的选择。财务数据是“证券代码+年份”的面板,并购事件是“证券代码+日期”的事件数据。我先把事件日期转成年份,再按证券代码和年份做一对多匹配:
stata复制use 并购清洗_中间文件.dta, clear
* 生成公告年份
gen deal_year = year(date0)
* 与财务数据按证券代码和年份匹配
merge m:1 证券代码 deal_year using 财务面板数据.dta, keep(match) keepusing(总资产 资产负债率 营业收入增长率 上市年龄 第一大股东持股比例)
注意几点:证券代码一定要统一成六位字符串,数字型的 600519 和字符串型的 "600519" 在 merge 时不会自动匹配;财务数据收盘年份不同,我用的是并购公告日前一年年报数据,避免使用当期数据导致反向因果。匹配完成后,立即用 tab _merge 检查丢失率。如果丢失率超过 10%,优先怀疑证券代码格式不一致,而不是财务数据缺失。
匹配完成后建议保留一份长面板数据:每个公司每年一条记录,标记当年是否发生并购、交易金额、支付方式等。这样后面做 DID 或者固定效应回归会方便很多。事件研究需要的日度股价数据则单独存放,不用合并进主面板。
3.4 连续变量处理细节
连续变量的处理直接关系到回归结果能不能发表。我处理这套数据时,对总资产、营业收入、交易金额这类右偏变量一律取对数;对 ROA、资产负债率、增长率等比率变量,先检查分布,再做上下 1% 的缩尾。
stata复制* 缩尾处理
winsor2 资产负债率 营业收入增长率 ROA, replace cuts(1 99)
* 查看分位数和极值
summarize 资产负债率, detail
很多人混淆 winsor 和 trim。winsor 是把极值替换为分位数,保留样本量;trim 是直接删掉极值,适合确实存在异常值的情况。并购研究中交易金额的极大值通常来自巨头之间的吸收合并,这些不是数据错误,所以尽量用 winsor 而不是 trim。
4. 实证分析:从事件研究到回归再到异质性
4.1 事件研究法计算 CAR
事件研究法听起来唬人,核心逻辑其实很简单:并购公告日前后,市场会给出“超额收益”,把它和正常收益模型比较,就得到 CAR。我用的是市场模型,估计窗口取 [-210, -11],事件窗口取 [-2, 2],这是并购研究里比较常见的默认参数。
Stata 里没有内置的 CAR 计算命令,我要么自己写循环,要么用 estudy 这类外部命令。自己写更可控,尤其是样本里大量事件日期重叠时,需要逐个公司逐日跑回归。简要流程如下:
stata复制* 假设已有日度数据:证券代码、date、ret(个股收益率)、mktrf(市场收益率)
* 生成事件日期变量 event_date
* 生成事件窗口标记
gen event_time = date - event_date
gen window_est = inrange(event_time, -210, -11)
gen window_event = inrange(event_time, -2, 2)
* 对每个事件估计市场模型并计算 CAR
tempname car_holder
postfile `car_holder' stkcd eventdate car using "car_results.dta", replace
levelsof 证券代码, local(stklist)
foreach stk of local stklist {
quietly reg ret mktrf if 证券代码 == `stk' & window_est == 1
predict ar_temp, residuals
* 事件窗口内累加
summarize ar_temp if 证券代码 == `stk' & window_event == 1
post `car_holder' (`stk') ("`=event_date'") (`=sum(ar_temp)') ...
}
这里有个容易踩的坑:事件窗口内计算 CAR 不能用回归的残差直接累加,因为残差的均值天然为零。我用的是预测收益做差,也就是实际收益减去市场模型拟合的期望收益,再累加。如果直接用 predict, residuals 在估计窗口内算,那只是回归残差,不代表事件窗口的超额收益。细节决定了结果是否经得起检验。
4.2 面板回归模型与固定效应处理
做完 CAR 之后,接下来就是主回归。常用的模型是:
code复制car_i = α + β * deal_characteristics_i + γ * controls_i + μ_ind + μ_year + ε_i
在面板数据和事件数据之间,我倾向把并购事件数据聚合成公司-年份面板,用 reghdfe 处理固定效应。reghdfe 是现在金融会计实证的标配,处理公司固定效应和年份固定效应非常快,标准误也能正确聚类到公司层面。
stata复制use 面板数据_最终版.dta, clear
* 设置面板
xtset 证券代码 year
* 主回归:以并购后一年 ROA 变化作为因变量
reghdfe dROA_post1 是否关联 现金支付 交易规模 是否跨界 公司规模 资产负债率 营业收入增长率, absorb(证券代码 year) vce(cluster 证券代码)
est store m1
* 输出结果
esttab m1 using 回归结果.rtf, replace b(%9.3f) se(%9.3f) star(* 0.10 ** 0.05 *** 0.01)
这里我特别提醒一点:聚类到公司层面后,如果样本里有些公司只有一次并购,个体固定效应会吃掉很多变异,导致 β 不显著。这种情况下可以考虑换用行业固定效应而不是公司固定效应,或者用随机效应模型配上聚类稳健标准误。不要把公司固定效应当成万能药。
4.3 亚组分析的实现方法
“stata 如何做亚组分析”是我被问得最多的问题。并购研究里常见的亚组包括:国企 vs 民企、同业 vs 跨界、现金支付 vs 股份支付、发达地区 vs 欠发达地区。亚组分析不只是简单分组跑两个回归,关键在比较组间系数差异是否显著。
如果只是把样本 split 然后再分别跑 reghdfe,看起来两组系数一大一小,但并不知道差异在统计上是否显著。我自己常用两种做法:
第一种是交互项法,在全样本中把分组虚拟变量与核心解释变量做交互,看交互项的显著性。这是最推荐的做法,样本量不损失,解释也直接。
第二种是组间系数差异检验,用 bdiff 命令做基于自助法的检验:
stata复制* 需要先设置样本拆分变量 group(1 表示国有,0 表示民营)
ssc install bdiff, replace
bdiff, group(group) model(reghdfe dROA_post1 是否关联 现金支付 公司规模, absorb(证券代码 year) vce(cluster 证券代码)) reps(500)
bdiff 会输出两组系数差异的经验 p 值。实操中如果某组样本量很小,500 次重复抽样可能不够稳定,建议至少 1000 次。注意亚组变量本身不能是模型中被 absorb 掉的固定效应,否则会出现完全共线性。
5. 常见报错与排查技巧实录
5.1 outcome does not vary 到底是什么意思
这个报错通常出现在 logit、xtlogit、xtreg 或部分 reghdfe 场景。字面意思是“因变量没有变异”。常见情况有两种:一是整个样本里因变量只有一个取值,比如你写的因变量表达式写错了,所有观测都是同一个数;二是在面板固定效应模型里,某个组内因变量完全不变,比如 xtlogit, fe 中,一个公司多年内 y 全是 0 或全是 1,这个公司会被自动剔除,如果这样的组占大多数,模型就无法收敛。
排查方法很直接:
stata复制tab 因变量
bysort 证券代码: egen group_sd = sd(因变量)
tab group_sd if group_sd == 0
如果发现大量组内标准差为 0,说明你的因变量在组内确实没有变化。这时候换成线性概率模型 LPM(用 reg 或 reghdfe 跑 0/1 因变量)通常能解决。很多做并购研究的人用 logit 模型预测“是否进行并购”,结果报错 outcome does not vary,大概率是样本里非并购年份占比太高,但固定效应模型里控制了很多不随时间变化的公司特征,导致模型识别不足。别硬刚,换成 LPM 加多重聚类标准误,结果一样能解释。
5.2 ftools 安装失败与 reghdfe 依赖问题
reghdfe 本身依赖 ftools、mata 等多个组件。很多时候你输入 ssc install reghdfe, replace 后提示成功,但真正运行 reghdfe 时却报错找不到 ftools。这是因为 reghdfe 安装时没有自动拉取依赖,或者拉取的是旧版本。
建议手动逐个安装,顺序很重要:
stata复制ssc install ftools, replace
ssc install moremata, replace
ssc install reghdfe, replace
装完后执行 reghdfe, version,如果正常显示版本号,说明工具链没问题。国内网络环境下,如果 ssc 一直超时,可以把默认安装路径指到本地镜像目录,再从镜像源同步。需要注意的是,不要用那种“非官方”的破解方式安装,很容易在后续数据处理时埋下隐患。
5.3 最大值最小值命令与缩尾处理
被问到的“stata 最大值最小值命令”,其实包含两类需求:一个是查看变量的最值,另一个是生成最大值最小值变量。查看用 summarize varname, detail,生成变量用:
stata复制egen max_roa = max(ROA), by(证券代码)
egen min_roa = min(ROA), by(证券代码)
egen 的 by() 选项很常用,用来生成每个公司的时间序列最大最小值。缩尾处理时,很多人上来就 winsor2 变量, replace,默认是 1% 和 99% 分位。这里要注意变量分布,如果是离散型的支付方式虚拟变量,千万不要缩尾。虚拟变量缩尾之后会出现 0 和 1 之外的小数,回归结果没法解释。我一般只对连续型财务变量缩尾,虚拟变量一律不动。
5.4 其他高频问题速查表
下面几个问题是并购数据实操里几乎每次都会遇到的,我整理成一个速查表。
| 报错/问题 | 常见原因 | 解决办法 |
|---|---|---|
| panel variable not set | 还没用 xtset 声明面板 | 执行 xtset 证券代码 year |
| repeated time values within panel | 公司-年份有重复记录 | duplicates drop 证券代码 year, force |
| variable not found | 变量名大小写或中文冲突 | 统一用英文小写,rename 处理 |
| merge 后 _merge 全是 1 或 2 | 匹配键格式不一致 | 检查证券代码是否字符串/数字混用 |
| 日期显示为数字 | 日期变量没 format | format date0 %td |
| 中文变量名乱码 | 编码问题 | 在导入时设置 UTF-8 或 gb18030 |
这些坑单独看都不难,但每个都能浪费一上午。做数据清洗时,我的习惯是每步操作完都 save 中间文件, replace,并留下一句注释说明这步在做什么,这样即使后面发现错误,也能快速回溯到出错步骤,而不是全部重新开始。
6. 扩展思路与我的实操体会
6.1 这套数据还能怎么扩展
并购数据最大的优势是扩展性强。目前这套 2005-2024 年的数据已经能覆盖绝大多数公司金融研究,但如果想做得更细,可以往几个方向延伸。
第一,加入文本分析。并购公告的文本长度、语气、风险提示数量、是否提到业绩承诺,都可以用 Python 或 Stata 的文本分析工具处理,生成公告信息含量变量。第二,加入 ESG 和供应链数据,研究 ESG 评级是否会降低并购的绩效波动,或者供应商集中度是否影响并购选择。第三,加入地区层面的数据,比如城市营商环境、产业政策支持力度,研究外部环境对并购绩效的调节作用。
这些扩展的共同前提,是保留好原始事件数据的所有字段。我见过不少人为了省事只留下核心变量,到后来想重新算一个交叉变量,却发现原始字段已经删了,只能回去重新导数据。所以在整理阶段就要尽量保留原始字段,用复制出来的工作变量做清洗,不要直接覆盖。
6.2 几点避坑心得
最后分享几个我反复踩过的坑。
第一,Stata 代码一定要模块化。我现在的做法是拆成 00_config.do、01_clean.do、02_merge.do、03_variables.do、04_regression.do。每个 do 文件开头用 clear all 和 set more off,结尾用 save 保存中间文件。这样即使某一步报错,也不需要从头跑。
第二,版本兼容问题。如果课题组里有人用 Stata 15,有人用 Stata 17,保存数据时统一用 save 文件名, replace 然后指定 version(15),避免高版本数据在低版本打不开。这不是小事,我吃过几次亏,教训很深。
第三,不要过度依赖 RD 或 DID 标签。并购事件不是随机分配的,用工具变量法时一定要找真正外生的工具变量,比如制度导致的税收成本冲击。别随便拿“同行业平均并购次数”做工具变量,审稿人一眼就能看穿,回归结果看起来显著,实际上完全站不住。
第四,随时记录变量定义手册。你用中文变量名还是英文变量名都行,关键是要有一个变量字典。并购数据变量太多,时间一长很容易忘记某个变量的生成逻辑。我一般会在每个 do 文件开头用注释写明变量含义和数据来源,这样半年后回头再看代码,还能知道当时在做什么。
做这套 2005-2024 年上市公司并购数据和 Stata 代码的过程,比我预想中琐碎得多,但完成之后带来的便利也是显而易见的。你拿到这套思路后,不管是直接复现回归,还是替换成自己的研究问题,都会省掉大量清洗时间。如果后面有具体某个环节报错,欢迎拿着报错信息来对照着查,大概率能在这篇文章里找到答案。
