这两天没加新功能,都在补一件事:让「行业」真正参与分析,而不只是个标签。
以前所有公司都走同一套判断逻辑,看毛利率、看负债率、看周转天数。问题是这些阈值本来就不通用——零售的 30% 负债率算健康,房企的 30% 等于没借钱。同一把尺子量所有人,结论必然是错的。
👉 进入财报实验室
一、先有骨架:10 个组 + 20 个门类
行业分类做成了两层:
| 层 | 内容 | 用途 |
|---|---|---|
| 行业组 | 10 个(制造、零售、地产、金融…) | 决定用哪套分析模板 |
| 国标门类 | 20 个(A 农林牧渔 → T 国际组织) | 对外可对齐 GB/T 4754 |
识别路径按可信度排:人工指定 > 代码映射表 > PDF 正文 > 公司名关键词。
后面这层「门类」是今天才补的。之前拿 20 个门类名字去跑,只认得出 13 个——M(科研)、N(水利环境)、O(居民服务)、Q(卫生)、R(文体)、S(公共管理)全部落空,因为它们只存在于服务业的中类里,没写进组识别的关键词表。
补完之后:
- 门类 → 组识别:19 / 20(T 国际组织不认,A 股没这种公司)
- 门类代码:20 / 20,每家公司的结果里都带
section
现在确认页可以直接选字母:
A · 农、林、牧、渔业C · 制造业M · 科学研究和技术服务业选完自动映射回行业组,页面显示「服务业 · 第三产业 · 门类 M」。
二、模板外置:阈值不该写死在代码里
原先判断逻辑是 if 毛利率 < 0.1: ...,散在几百行里。改一个阈值要动代码、跑测试、重启服务。
现在全部搬进 JSON:
{ "id": "inventory_heavy", "angle": "库存型", "when": [{ "metric": "存货周转天数", "op": ">=", "value": 180 }], "text": "存货周转 <b>{存货周转天数}</b> 天:货压在仓库里..."}规则引擎支持 >= > <= < == != exists truthy,文案里能直接引用指标并指定格式(百分比 / 亿元 / 天数 / 倍数)。行业模板用 extends 继承通用规则,只写差异部分。
一个副作用:配置坏了程序不会崩,会回退到旧逻辑并打警告——宁可分析得糙一点,也不能让整份年报解析失败。
三、识别 PDF 正文:从 60% 到能用
代码映射表覆盖不到的公司,只能去年报正文里找「所属行业」这类句子。
第一版扫前 15 页,50 份抽样只认出 60%。漏掉的原因很朴素:有的公司把行业信息写在第 21 页。
改成两遍扫描(15 页 → 30 页)就上去了。同时还修了几个假阳性:
- 「1、新一代信息技术产业」→ 序号开头的句子不算
- 「集成电路作为信息技术产业」→ 泛指句式不算
- 「金融等众多行业」→ 带「等」的泛指不算
现在这条路是 60% 召回、0 错误——宁可认不出,也不要认错。
四、金融三业:报表结构完全不同
制造业那套解析器遇到银行直接崩:银行没有「营业成本」,保险没有「毛利」,证券的科目名是另一套。
所以单独写了金融解析器,按三个行业各自的恒等式校验:
| 行业 | 恒等式 |
|---|---|
| 银行 | 8 条(资产=负债+权益、利息净收入拆分…) |
| 证券 | 8 条 |
| 保险 | 5 条(保险不查收入拆分) |
结果是银行 8/8、证券 8/8、保险里的人寿 5/5 全部通过。平安卡在一个 5,815 元的差额上,至今没修——差一分钱都不出页,这是有意的。
五、诊断:从「三个数」到「为什么亏」
最初的分析输出是这样的:
毛利率 33.48%、净利率 -8.44%、期间费用率 73.76%
三个数字摆出来,读者还得自己算哪个是主因。现在的输出是结论先行:
收入同比 17.06% 到 6.62 亿元,仍亏 0.56 亿元——毛利撑不住费用。毛利 2.21 亿、期间费用 4.88 亿。
判断「为什么亏」的规则改过一次。第一版按固定顺序试:负毛利 → 费用吞噬 → 减值 → 投资。结果福星股份亏 41.82 亿,被判成「费用吞噬」——因为毛利确实覆盖不了费用(缺口 2.64 亿),可真正的窟窿是 25.48 亿减值。
改成按占比判:费用缺口、减值、投资亏损,谁占亏损额比例最大,谁就是主因。现在福星是「减值拖累型」,岩山科技是「费用吞噬型」(毛利 2.21 亿 vs 费用 4.88 亿),各归各位。
六、全量回测:61 份年报
这两天最重要的一个数字,是拿 61 份真实年报跑了一遍完整链路:
| 指标 | 结果 |
|---|---|
| 样本 | 61 份 |
| 通过 | 57 份(93.4%) |
| 修完权益校正 | 58 份(95.1%) |
| 剩余失败 | 3 份(各差百万到千万级) |
失败的全被恒等式拦下来了,没有一份错误页面流出去。
最值得说的一个 bug:山子高科差了 63 亿。查下来是「所有者权益」抓到了权益变动表里的同名行(74.83 亿),而资产负债表里归母 11.59 + 少数 0.01 = 11.60 亿,正好等于资产 108.85 − 负债 97.25。
修法是:当归母+少数与「资产−负债」一致、却与已解析的权益不符时,用前者覆盖。两个来源交叉验证,比任何单点解析都可靠。
七、顺手把界面换了
原来是蓝粉渐变的 SaaS 风,看久了和「财报」这件事不太搭。换成了一套更安静的语言:暖石色背景、单一青色强调、1px 细线做结构、标题用细字重。
改的不只是颜色:
- 控制台上传时显示真实百分比进度,解析时显示已用时间(30–60 秒不让你干等)
/admin收进后台——经 Cloudflare 隧道访问一律 404,只能本机直连- 年报页的「两年对比」「关键比率」加了图:一个同比变化率横条,一个按单位拆开的分组条形(比率和天数不能画在同一根轴上,否则流动比率 0.83 会被压成一条线)
八、还没做完的
- 教育、医疗、环保公用三个细分模板刚写完,阈值是行业常识 + 单一样本定的,等真实年报来打脸
- 中天精装差 36.5 万、平安差 5,815 元,都是小数目但没修
- 厦门银行的 PDF 文本层是散的,暂时解析不了
两天下来最大的感受:财报分析的可信度不来自模型多聪明,来自每一处都能被验证。资产必须等于负债加权益,现金流必须首尾闭合,差一分钱就拒绝出页——这条规则救回来的错误,比任何算法优化都多。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时