
做港股上市公司盈利分析的第一步从来都不是跑模型而是先解决数据问题。我过去几年一直在和各类公司财务数据打交道经常遇到一种情况从数据库里导出“利润表”满心欢喜开始跑回归结果发现科目口径对不上、财年错位、币种混用整套结果推倒重来。直到我认真用了一遍CnOpenData 港股上市公司利润表数据集才真正把这条流水线理顺。这篇文章以我自己的实际使用经历为线索把港股利润表的科目逻辑和A股差在哪、CnOpenData 这个数据集到底做了什么标准化、拿到手之后怎么读取和计算常用指标、以及几类非常容易踩的坑一次性说清楚。不管你是做学术研究的硕博生、搞基本面量化的分析师还是刚开始接触港股财务数据的学生下面的内容基本都能直接照着用。1. 港股利润表和A股根本不是同一套玩法科目体系的底层差异1.1 港股利润表的完整链路从营业额到每股盈利先说最基础的问题港股上市公司披露的利润表长什么样港股财报遵循香港财务报告准则HKFRS和内地企业会计准则CAS在“要不要单列某些科目”“某些科目怎么归并”上差别很大。一份典型的港股合并利润表从上到下大概是这么一条链路营业额Turnover部分公司叫“收益”“收入”或“总收入”销售成本Cost of Sales毛利Gross Profit其他收入及收益Other Income and Gains销售及分销成本Selling and Distribution Expenses行政开支Administrative Expenses财务成本Finance Costs应占联营公司及合营企业损益Share of Results of Associates and Joint Ventures除税前溢利Profit before Taxation税项Taxation期内溢利Profit for the Period本公司拥有人应占溢利Profit Attributable to Owners of the Company非控股权益应占溢利Profit Attributable to Non-controlling Interests每股盈利Earnings per Share注意“溢利”就是内地说的“利润”“除税前溢利”类似A股的利润总额。港股最底部一定给两行归属股东应占和少数股东应占而且每股盈利是只按“股东应占溢利”来算的。1.2 和A股对比差在哪几个关键位置内地A股上市公司按《一般企业财务报表格式》披露利润表大致是营业收入含主营业务收入、其他业务收入营业成本税金及附加销售费用、管理费用、研发费用、财务费用其他收益、投资收益、公允价值变动损益营业利润营业外收入、营业外支出利润总额所得税费用净利润归属于母公司股东/少数股东逐项对过去差异非常明显。研发费用A股从2018年起把研发费用从管理费用里单列出来港股绝大多数公司没有这一行研发开支要么塞在行政开支里要么只在附注里单独披露。所以如果直接用港股利润表做“研发强度”分析这个字段大概率是空的得去附注找。税金及附加A股利润表里“税金及附加”是营业收入的减项港股利润表上没有这个单列项消费税、城建税等通常归入销售成本或行政开支。这导致同样一家公司在两地报表里毛利口径会有差异。其他收入的范围港股利润表里的“其他收入及收益”是个大筐政府补助、利息收入、租金收入、汇兑收益、出售资产收益都可能装在里面并不等于A股“其他收益”科目。做盈利质量分析时不拆出来很容易高估经常性盈利能力。应占联营公司损益A股把按权益法核算的联营企业投资收益放在“投资收益”里港股直接单列一行“应占联营公司及合营企业损益”位置在财务成本之后、除税前溢利之前性质上属于投资收益但不并入营业利润。营业利润这个“中间量”A股利润表有“营业利润”这个环节港股没有统一要求。很多港股公司会在毛利和除税前溢利之间标一个“经营溢利”Operating Profit但这不是所有公司都列口径也不完全一样。跨公司比较时最好自己重新定义“经营溢利 毛利 其他收入 - 销售分销成本 - 行政开支”而不是直接用披露值。1.3 为什么不能直接拿原始年报来做研究有人会说“这些差异我都能处理直接去港交所披露易下载年报手工录不行吗”理论上可以实操上非常痛苦。不同公司的科目命名不统一。同样是收入有的公司写“营业额”有的写“收益”有的用英文Revenue有的用Turnover。老牌港资公司偏向用“营业额”新经济公司更爱用“收入”如果不做同义词映射Excel里光是VLOOKUP都要断好几层。不同公司会自行调整披露层级。港交所对利润表披露格式没有像内地那样严格的统一模板公司可以在准则允许范围内增删科目。比如有些地产公司会单列“投资物业公允价值变动收益”有些把它挤在“其他收入”里这直接影响到利润结构分析的准确性。很少人意识到港股并不要求所有上市公司都用HKFRS。部分公司按IFRS编报和HKFRS实质趋同但科目名称可能不同还有少数在港上市的内地公司直接按CAS编报利润表长得和A股一模一样这对“统一处理”造成很大干扰。所以核心矛盾是港股利润表的原始披露“长得太自由”而研究工作需要的是“整齐划一的字段”。把原始披露变成整齐划一的字段本身是个费时费力又不产生研究价值的环节而标准化数据集存在的意义就是替你把这个环节做掉。2. CnOpenData 港股利润表数据集它把标准化做到了哪一步2.1 先搞清楚这张表的覆盖范围我拿到的 CnOpenData 港股上市公司利润表数据覆盖了港交所主板和创业板GEM的全部上市公司时间跨度从各公司上市披露首份年报开始一直滚动更新到最近一个完整财报期。需要说明的是不同版本的数据在起始年份上可能不一样早年间港交所电子化披露不完善部分早期数据只覆盖到2000年前后用的时候第一步先画出“公司数×年份”的覆盖图确认样本范围再往下走。数据来源以公司在港交所披露的定期报告为主包括年报、中报以及创业板公司需要披露的季度报告同时经过整理和交叉校验。CnOpenData 的做法比较接近“学术数据库”的定位他们不做原始文本堆砌而是把报表里的信息结构化输出成能直接读入统计软件的表。2.2 标准化字段的设计逻辑这个数据集最值钱的地方不是“把数字从PDF里扒出来”而是它把几百家公司的披露习惯统一成一套字段体系。我实际使用下来标准化逻辑主要体现在以下五条第一科目归并。原始报表里“营业额”“收益”“收入”“Revenue”统一归到同一个收入字段不同公司对销售成本的叫法差异也被抹平。第二利润链路一步不跳。从营业收入开始、销售成本、毛利、各类费用、财务成本、应占联营损益、除税前溢利、税项、净利润、归母净利润每个环节都有一个对应字段。这样做的好处是计算任何中间指标都不用回头翻原始报表。第三币种字段保留。港股公司里有很多收入以人民币计价、报表以港元列示数据表里会保留原始币种标记方便用户按需换算。第四基础信息配套。每行记录不只是一个数字而是带着股票代码、公司简称、行业分类、财报期截止日、报告类型年报/中报/季报、公告日期、审计意见等配套信息。这些看起来不起眼却是后面做面板数据合并的关键键值。第五归属口径明确。净利润会区分“归母净利润”和“少数股东损益”每股收益提供基本和稀释两列避免用户自己从净利润推算时出错。提示实际字段名在不同版本的数据文档里可能有差异拿到数据后先找对应的字段说明表把“中文字段名—英文变量名—原始报表科目”三者对应关系核对一遍再用。2.3 和“自己爬数据”“商业终端导出”的对比我身边不少朋友喜欢自己从披露易爬年报或者找商业金融终端导出。这条路到底值不值得走我给一个直观对比方案时间成本资金成本标准化程度可复现性数据溯源自己爬披露易手工录入极高低低差清楚商业终端导出低非常高中等一般不好回溯科研数据平台低中等高好有版本可追溯商业终端导出的最大问题是它更像“查询快照”今天导和三个月后导可能拿到不同版本而且终端字段是面向投资分析的不一定按学术研究的口径定义字段自己爬数据虽然灵活但一个年报季几千份财报的清洗工作量非常夸张而且年报PDF格式五花八门解析错误率很难控制。科研数据平台的优势在于“批量标准化”它已经把跨公司、跨年度的口径统一好了还保留底层版本信息这对实证研究的可复现性特别重要。当然也有代价——如果想拿某个细分科目比如从“其他收入”里拆出政府补助标准化表通常帮不了你还得回原始报表。3. 拿到表格先核对一遍核心字段逐个拆解3.1 从营业收入到净利润每个环节对应什么拿到数据之后先别急着算指标把核心字段都过一遍。以我常用的字段清单为例大体可以分成四组收入与毛利层营业收入、销售成本、毛利。毛利率 营业收入 - 销售成本/ 营业收入这里的营业收入是“合并利润表口径的营业总收入”。有些港股公司会把利息收入做到“其他收入”如果你的研究对象是银行或金融公司这个字段的语义完全不同要格外小心。费用与经营利润层销售及分销费用、行政开支、财务成本、其他收入。港股普遍没有“研发费用”字段如果研究需要一般通过附注补。这里的“财务成本”对应A股的“财务费用”但内容更窄通常只包括利息支出和融资成本汇兑损益经常被归入其他收入或行政开支。税前利润与净利润层除税前溢利、所得税费用、净利润、归母净利润、少数股东损益。“除税前溢利”和A股“利润总额”基本对应但税项的包含范围在两个准则下有细微差异主要影响在递延所得税的披露习惯上。每股指标层基本每股收益、稀释每股收益。港股利润表会把“每股盈利”单独列示部分公司还会披露“每股股息”。注意每股收益的分子只能是“归母净利润”用净利润总额算出来的结果会和披露值对不上。3.2 港股特色科目别拿A股习惯去套数据里有两个港股特色科目值得单独强调。第一个是“应占联营公司与合营企业损益”。它代表公司按权益法核算的联合营企业投资收益在利润表上排在财务成本之后、除税前溢利之前。这个科目的波动性通常很高如果某一年联营公司爆雷它会直接把利润表拉下来一大截。做盈利预测时我习惯把“经常性经营利润”和“应占联营损益”分开看不要把联营公司的利润波动当成主营业务波动。第二个是“其他收入及收益”里的公允价值变动、投资物业重估、出售资产收益等项目。港股对投资物业的公允价值变动非常普遍房地产公司经常用这个科目调节利润。标准化数据集通常不会帮你把这些细分项拆出来想区分“经营利润”和“非经营利润”要么在数据里寻找更细的字段要么回去查附注。3.3 容易被忽略的基础信息字段很多人在数据清洗阶段只保留代码和数字把“财年截止日”“报告类型”“公告日期”这些字段直接扔掉这是非常可惜的。“财年截止日”决定你以后所有同比、面板匹配的基准。港股上市公司财年截止日非常分散大量内地企业是12月31日但不少香港本地企业、部分消费品和零售公司选择3月31日、6月30日或9月30日。如果不引入财年截止日这个字段后面做跨公司同比时一定会错配。“报告类型”区分年报、中报和季报也决定了你能用多高频率的数据做分析。港股主板绝大多数公司只有年度和中期报告季度报告不是强制性义务创业板公司才有季度披露要求。如果想做高频盈利预测基于港股利润表的频率上限大概就是“半年”。“公告日期”则和“财报期截止日”是两个概念。年报可能在财年结束后三个月才披露和公司实际经营表现之间存在时滞事件研究时用公告日期面板匹配时用财报期截止日两者混用会出现严重的时间错位。4. 实操用Python把利润表变成可分析的指标4.1 读取数据后的第一轮体检拿到数据后我的第一个操作永远是“描述性体检”。下面是一段最基础的 Pandas 代码import pandas as pd df pd.read_csv(hk_income_statement.csv, dtype{stock_code: str}) print(总行数:, df.shape[0]) print(上市公司数:, df[stock_code].nunique()) print(报告类型分布:\n, df[report_type].value_counts()) # 财年截止月分布 df[fiscal_end_month] df[fiscal_period_end].str[-2:].astype(int) print(财年截止月分布:\n, df[fiscal_end_month].value_counts().head(10)) # 空值率检查 missing_ratio df.isnull().mean().sort_values(ascendingFalse) print(主要字段空值率:\n, missing_ratio[missing_ratio 0.1])这段代码做的事很朴素看有多少公司、有多少年报和季报、财年截止日集中不集中在12月、哪些字段空值率过高。我几乎每次拿到一个新的财务数据集都要跑一遍这个流程能在半小时内发现数据文件里潜在的大问题。如果发现某些年份公司数量出现断崖式下跌那不是退市潮而是数据覆盖还没补齐如果发现“归母净利润”空值率高到20%那就要确认是不是只在部分报告类型里披露。这些问题越早暴露越好等回归结果跑出来再发现就晚了。4.2 毛利率、净利率和同比增速怎么算体检没问题之后就可以构造常用盈利指标了。这里给一套可以直接抄的代码但字段名以你自己下载的数据版本为准。# 构造毛利率注意先处理0值和缺失收入 df[gross_margin] (df[revenue] - df[cost_of_sales]) / df[revenue] # 归母净利润率 df[net_margin_parent] df[net_profit_parent] / df[revenue] # 按公司分组按财年截止日排序算同比增速 df df.sort_values([stock_code, fiscal_period_end]) df[revenue_yoy] df.groupby(stock_code)[revenue].pct_change() # 当年和上一年财年截止日相差超过365天的同比口径需要特别标记 df[period_diff_days] df.groupby(stock_code)[fiscal_period_end].diff().dt.days df.loc[df[period_diff_days] 380, revenue_yoy] None最后一步很多人会漏港股公司财年截止日不统一如果直接把上一行当作“上一年”那么一家3月31日截止的公司和一个12月31日截止的公司效果都正确但如果有公司变更财年截止日有公司从12月改为6月导致中间出现一个短财年或长财年pct_change 就会算出一个没有实际意义的“同比增速”。所以我用财年截止日间隔做了过滤超过380天的视为财年变更不放进同比计算。毛利、净利率是最基础的盈利指标再进阶一点EBITDA 需要加回折旧摊销但港股利润表正文通常不直接披露折旧摊销一般放在现金流量表或附注里。如果 CnOpenData 这份表里没有单独的折旧摊销字段就需要用另一张“现金流量表”数据表去补不要凭空在利润表数据里找。4.3 和其他表拼接时最容易出问题的地方学术研究很少只用利润表通常还要关联资产负债表和现金流量表。拼接时最常出问题的是“键值”的格式。港股公司的股票代码在不同数据源里格式完全不同披露易里常见的是腾讯控股00700.HK某些终端里可能是“700.HK”另有数据源不带后缀直接给“00700”。如果直接用字符串去匹配极容易把所有代码都匹配不上。我的解决方法是写一个统一的标准化函数def normalize_hk_code(raw_code: str) - str: # 去掉空格、点和.HK后缀补齐5位数字 code raw_code.strip().replace(.HK, ).replace(HK, ).split(.)[0] return code.zfill(5) df[stock_code] df[stock_code].apply(normalize_hk_code)另外同一家公司在同一财报期出现两条记录时不要默认是重复数据。先检查是不是“重述版本”或“不同披露口径”——公司年报发布后发生会计差错更正、追溯调整数据会更新一版保留哪一版取决于你的研究目的做当期基本面分析用最新重述版做信息披露与市场反应研究用首次公告版。5. 复盘五个真实踩坑港股利润表数据常见误用5.1 财年截止日不统一同比增速算错一整个样本我最早用港股数据时犯过一个典型的错误直接从一张表里按“年份”字段筛出2021和2022然后相减算同比。结果一个样本里混进了两种公司财年截止于2021年12月31日的和财年截止于2022年3月31日的。前者代表“截至2021年底的经营”后者已经包含2022年第一季度完全不是同一时期却被当成同一年的数据去比较。后来我形成习惯所有跨公司比较都基于财年标识而不是自然年标识。用数据里自带的财年字段生成一个“fiscal_year”变量例如把截止日在2021年4月至2022年3月的公司统一标记为FY2022再在这个基础上做面板匹配。不同公司的经营季节性差异是存在的但只要统一按“各自财年内的完整经营周期”来对齐时间错配的问题就基本消掉了。5.2 港元人民币混着算汇率波动变成经营波动港股上市公司里很多内地公司以人民币为记账本位币但报表以港元列示另有一些公司直接用人民币列示。这意味着同一张标准化的利润表里不同公司的“营业收入”背后是不同币种直接横向比较会把汇率波动全部混进经营差异里。稳妥的做法分两步先看数据表里每一行有没有币种标记例如“report_currency”字段把标记为HKD和CNY的记录分开再选定统一比较基准如果研究视角是“人民币口径的盈利能力”就把港元数字按财报期平均汇率转换成人民币反之亦然。这里要注意不能用某个时刻的即期汇率去转全年利润表至少得用年度平均汇率否则误差会被季节性盈利放大。如果数据表里已经提供“原始币种金额”和“换算后金额”两套字段那么优先使用后者做跨公司比较如果只有单一币种字段建议随机抽几家公司和原始年报数字核对一遍确认平台做换算时采用的汇率规则。5.3 把银行和保险公司混进一般行业指标“毛利率”这个指标对银行、保险公司来说本质上不成立。银行的利润表核心结构是利息净收入、手续费及佣金净收入、经营费用、减值损失保险公司的核心是已赚保费、赔付支出、提取保险责任准备金。把银行放进“毛利率排名”里得到的结果不但没有意义还会污染整个样本的描述统计。我的习惯是在做全样本分析之前先把金融行业单独筛出来。判别依据不是公司简称而是行业分类字段。恒生行业分类系统HSIC里的“金融业”大类包括银行、保险、其他金融又或者用GICS分类里的Financials效果类似。不同数据平台给港股配的行业体系可能不同但处理原则一致金融行业单独建模、单独展示、单独解释。另外注意很多港股公司名字里带有“金融”或“银行”但实际是控股公司利润表结构偏投资控股而非银行业务。观察“营业收入”里的利息收入占比和“应占联营损益”占比能更好判断实际业务属性。5.4 追溯重述把新旧版本混在一起上市公司发布年报时如果同期对以前年度报表做了追溯调整一段时间后你会看到同一个公司、同一个财年出现两套不同的净利润数据。比如某公司因会计政策变更把之前五年的折旧方法调整导致前一年净利润发生变化。数据平台在抓取时会更新记录如果原用户已经下载过旧版本就容易出现新旧版本混用。我经历过一次很尴尬的事同一个样本里同一家公司的FY2019净利润一部分行是原始披露值另一部分行是重述后的值面板里出现时间序列跳动。后来我学到的做法是看数据里有没有“公告日期”或“更新版本”字段按“同一股票代码同一财年标识”分组保留公告日期最新的记录如果做事件研究则反过来保留首次公告日期那条记录。5.5 中报数据被过度使用或完全忽略港股主板公司没有强制季报义务所以基于利润表数据一年最多只有两个观测点年报和中报。有些研究者为了拉长面板把中报数据也放进同一套年度指标里却忘了中报是没有经过完整年度审计的、而且部分公司的中报披露质量远低于年报。另一些人则彻底忽略中报只用年报。这在某些场景下会损失重要信息比如一家港股公司在前一年年报亏损、当年中报已经大幅扭亏市场会沿着中报重新定价如果忽略中报你对这家公司“盈利拐点”的判断会晚半年。我的折中方案是在做年度面板时只用年报做盈利动量分析或事件研究时引入中报但单独设置“report_type”变量在模型里控制报告频率差异带来的噪音。6. 对这份数据集的使用体会与落地建议6.1 用好这个数据集的三个习惯第一永远在数据清洗前做覆盖体检。我用 CnOpenData 这类数据集的第一天都会画出“公司数量×年份”的柱状图再多看一眼财年截止日的分布。这种看似低级的检查能帮你躲开数据版本不完整带来的大坑。第二挑几家自己熟悉的大公司做锚点。我会把腾讯、汇丰这类头部公司的历年利润表从数据集里抽出来和原始年报上的数字逐行核对一遍。如果锚点公司对得上说明数据质量大概率是稳的如果锚点都对不上那这个数据集就不适合你的研究场景。第三保留“字段映射底稿”。处理港股利润表时我始终会在旁边放一份“原始报表科目—标准化字段”的对应关系表。这份底稿在写论文的数据章节时非常有用审稿人问起具体口径时直接拿得出来后续做稳健性检验也可以快速回溯替换字段。6.2 什么时候该回到原始报表核对标准化数据集不是万能的。当你发现某个指标异常比如某家公司毛利率突然从30%跳到60%不要直接当异常值删掉先回去查原始年报。有时候是资产重估、有时候是合并范围变更、有时候是会计政策调整这些“异常”本身就是重要的研究信息盲目删掉反而会漏掉关键信号。我个人的操作准则是数据集负责规模和效率原始报表负责解释和验证。先用手头标准化的 CnOpenData 港股上市公司利润表跑通全流程锁定关键样本和疑点再回到港交所披露易调出对应公司的原始PDF逐条确认科目和计量口径。这套流程看起来多了一道手工作业但恰恰是这道工序让我的实证结果经得起复现和审视。如果你正准备用港股利润表做研究我的建议是从一张年报开始把从原始报表到标准化字段的链路走通一遍再扩展到全样本。一次性拿到几百家公司的数字确实方便但真正理解每一个字段背后的含义才是保证后续所有分析不出结构性错误的前提。