从0到1搭一个「A股财报打假 + 公众号成稿」平台:可复用的操作手册
ashare-forensic-report skill。本文把架构、最易做错的设计决策、发布流程、踩过的坑串成手册,下次构建照着做即可,不用再踩一遍。一、平台能做什么
输入任意 6 位 A 股代码,平台自动完成四步并产出三块内容:
•财报真实性:18 项打假检验逐条给依据,输出 0–98 可信度评分与等级(高可信 / 较可信 / 存在疑点 / 重大疑点)。
•财务表现:杜邦拆解、成长性、现金流含金量、自由现金流。
•估值与同业:同业对比(自动窄→宽回退)+ 估值结论(偏贵 / 合理 / 便宜)。
•公众号文章:直接可发的 Markdown + 微信 HTML,含标题、摘要、正文,一键推草稿箱。
二、整体架构(4 层)
engine/fetch.js | https 请求,cache/ 缓存 6h | |
engine/forensics.jsmetrics.js | ||
server.js | http,端口 8848,SSE 流式 | |
public/ |
服务路由:/api/search(搜股票)、/api/analyze(完整 JSON)、/api/analyze-stream(SSE,前端实际用的就是这个)。
三、数据从哪来(第一个大坑)
数据源是东方财富公开接口(datacenter 镜像 + push2 行情),无需 key,但有几个硬约束:
•必须用 Node 原生 https 自定义请求,不能用 fetch / undici——部分东财节点会把 undici 连接直接 RST 掉。
•响应体是 gzip / deflate / brotli,要解码;原始响应缓到 cache/,改引擎逻辑不用清 cache,重启即生效。
•金融股走 ORG_PREFIX 路由(通用 G / 银行 B / 证券 S / 保险 I)。银行利润表缺 TOTAL_OPERATE_INCOME,映射时用 ?? N(OPERATE_INCOME) 兜底,杜邦和比率才不会全部失效。
四、财务真实性引擎(核心,最易做错)
4.1 18 项检验清单
检验分两类作用域,权重合计 125:
| quality(金融股 NA) | |||
| quality |
4.2 两作用域分离(authenticity vs quality)
这是最反直觉、也最不能错的一点:
•authenticity(真不真):审计意见、利润含金量、应收/存货异动等,计入可信度。
•quality(好不好):Piotroski F-Score、Altman Z-Score 只计经营质量,绝不计入可信度。
原因:周期底部的好公司 F-Score 也会掉到 2/9、Z 值偏低,若计入可信度,会把"如实披露但经营困难"的公司(如万科)误判成"财务造假"。万科就是这么从 29 分掰回 59.7 的。
4.3 可信度 = 加权均值 − 超线性惩罚(不要纯平均)
绝不能用纯加权平均——它会稀释致命红旗。康美药业净现比 -0.22、Z 值转负,纯平均仍得 76 分;改用超线性惩罚后落到 63.6,分层才合理。上限封 98 是因为公开数据只能"证伪"不能"证真",给 100 会误导。
4.4 审计意见三档 + 历史出清
关键补丁:历史出清判定——若最新年报已恢复标准无保留且连续 ≥2 年,旧的非标降级为 WARN,不把已整改公司钉死。中利集团 2021–2022 是保留意见、2024–2025 已恢复干净,文章标题就不能再写"会计师没敢签标准无保留"。
4.5 亏损公司净现比无意义
累计净利润为负时,净现比(如 -9.69)完全失去意义,应显示"不适用"。而且"账面亏但经营现金流为正"是正向信号(说明造血能力未枯竭、亏损多是非现金减值),绝不能写成"利润与现金严重脱节"。
4.6 金融股三项 NA
收现比、大存大贷、Altman 对银行 / 证券 / 保险直接返回 NA 并说明原因:银行没有"销售商品收到的现金"科目、存款是负债不是闲置资金、Altman 模型是为制造业校准的。
五、估值与同业
同业样本过窄是个常见陷阱:比亚迪的板块"电动乘用车"只有 3 家,中位数会被少数名字带偏。解决方案是窄→宽回退:Ⅲ级行业 → Ⅱ级行业,样本达到 MIN_PEERS = 8 即停。同时用 NON_INDUSTRY 正则过滤概念 / 板块 / 地域 / 风格 / 破净 / 龙头等非行业板块。
六、文章生成(微信合规)
平台产出 wechatHtml,但推前必须净化:
•去掉 linear-gradient:微信会直接拒收报 45166,文本高亮换成纯色底。
•确保无 box-shadow / <script> / <style> / 锚点 href="#"。
标题要压缩:微信标题 ≤ 64 字节,平台推荐标题常超(如 87 字节),需压成短标题,但正文 H1 保留完整标题。封面由 push_draft.py 取 skill 目录里最新修改的 png,推送前务必 touch 指定封面,否则会误套目录里最新的太极图。
七、公众号发布流程
•用 mp-draft-push skill 的 push_draft.py(入参:标题 / 摘要 / HTML 文件 / 封面)。
•多账号隔离:每个号一份 .env(如 .env / .env.fusilu),不要互相覆盖。
•⚠️ 血泪教训:push_draft.py 原本硬编码只认 .env,父进程传的环境变量它不认。结果 --env .env.fusilu 实际还是推回了默认号。修复办法是改成"优先读环境变量、没有再读 .env"。发布前务必核对目标账号的 AppID,避免推错号后在错误草稿箱留一堆重复稿。
八、校准方法论(验证平台没做错)
选 10 只分层样本跑回归,期望可信度严格有序:
用 _verify.js(或 skill 里的 scripts/verify.js)跑这组,确认无分层回退。字段契约要 100% 完整(前端引用路径无缺漏)。
九、常见坑速查
•端口占用:netstat -ano | grep :8848 找 PID,taskkill /PID x /F(bash 的 taskkill 有时不生效,改用 PowerShell Stop-Process)。
•沙箱删缓存被拦:批量 rm cache(300+ 文件)会触发 SAFE_DELETE_BULK_CONFIRM_REQUIRED。cache 是响应层,改引擎不用清,直接重启。
•跨命令临时文件不留存:写文件要在同一条沙箱命令里完成,或加 dangerouslyDisableSandbox。
•微信禁忌:linear-gradient / box-shadow / <script> / 锚点。
十、下次构建一键复用清单
加载 ashare-forensic-report skill(含 SKILL.md + references/api_reference.md + references/engine_reference.md + scripts/verify.js),它把上面所有非显然决策都固化了。
复制 engine/ 三件套 + server.js + public/ 三件套。
配置 mp-draft-push 多账号 .env,并确认 push_draft.py 优先读环境变量。
启动:PORT=8848 node server.js。
浏览器开 http://localhost:8848/?code=600519 验收四个标签页。


