company-research
A 股个股研究六阶段 SOP(profile → financials → estimates → valuation → risk → report),Phase 0 范围 = 财务估值闭环。当任务是研究 / 分析 / 评估一只或多只**已指定代码**的 A 股个股时使用;规定每阶段取哪些数据、调哪些 calc 函数、必须落盘什么产物、过什么 Gate。不用于:从市场中筛选标的、泛行业讨论、概念解释、给投资动作建议。
git clone --depth 1 https://github.com/simonlin1212/Vibe-Research /tmp/company-research && cp -r /tmp/company-research/.agents/skills/company-research ~/.claude/skills/company-researchSKILL.md
# 个股研究 SOP(company-research)
前置:AGENTS.md 是最高纪律(三条不可越线 / 五问 Gate / 估值口径 / 落盘契约),本 SOP 只规定流程。
取数一律用 `data-access` skill 的脚本(`.agents/skills/data-access/scripts/`);**由编排器在每个阶段开始前执行**(手工运行时由研究者执行),脚本输出落在 `RUN/fetch/<script>.json`,执行账本在 `RUN/fetch/_ledger.json`——agent 只读取这些结果,**不得自行运行取数脚本**(取数与解释分阶段,AGENTS.md §5)。计算一律用 `calc/cli.py`(函数契约见 `calc/SPEC.md`),自己不做任何算术、不做单位换算。
**Phase 0 范围声明**:本 SOP 当前交付的是"财务估值闭环"——行情 / 财务 / 一致预期 / 估值 / 公告线索。不可替代性与产业链位置的证据(产能 / 客户认证 / 良率 / 专利)需要 industry-chain 等 Phase 1 skill;Phase 0 下该项只能标 `待补`,报告必须明示"不可替代性未验证"。
## 0. 开工三件事
1. 解析标的:代码 → 6 位 + 市场(SH/SZ/BJ),由任一 data-access 脚本完成校验。**只给名称不给代码时,要求用户提供代码**(Phase 0 没有登记的名称解析脚本,不得自造反查)。解析失败就停,不猜。
2. 确定运行目录 `.local/runs/<run-id>/`(编排器给定;手工运行用 `YYYYMMDD-HHMMSS-<symbol>`);取数脚本由编排器 / 研究者传 `--out-dir` 执行(原始响应进 `raw/`,结构化输出进 `fetch/`),calc 一律传 `--run-dir`,每次计算一个文件写入 `calcs/`。
3. 若 `knowledge/companies/` 有该标的档案:只作线索读取(status 为 stale / refuted 的不得引用为事实);其中历史结论与本次实时数据冲突时,必须用实时数据反证并在报告"风险与反证"中写明,不顺从旧结论。
## 1. 六阶段(顺序固定,每阶段结束过 Gate 才进下一阶段)
| 阶段 | 目标 | 取数脚本(必需 ★ / 可选 ○) | calc 函数(输入口径) | 阶段 Gate(不过 = 补跑或标缺口) |
|---|---|---|---|---|
| profile | 公司是谁、上市状态、市值、报价是否可用 | ★ fetch_profile ★ fetch_quote ★ fetch_trade_calendar(读其结果) | — | 名称 / 市场 / 上市状态齐;报价通过 §2 依赖矩阵的 stale 判定(非盘前的 is_stale=true 不通过;盘前且日期吻合可按昨收继续);不可替代性标签 `tech_moat / capacity_moat / both / 待补`(Phase 0 通常为 待补,须明示) |
| financials | 近 8–12 报告期的营收 / 归母 / 扣非(累计值)→ 单季、TTM | ★ fetch_financials | `quarterize`(对 revenue_cum / net_profit_parent_cum / net_profit_deducted_cum 各跑一次,unit=元, money=true)→ `latest_quarter`(扣非,unit=元, money=true)→ `ttm_sum`(归母与扣非各一次,money=true,作 ttm_yoy 与 TTM PE 交叉验证的中间量)→ `ttm_yoy`(**主用归母净利润**,money=true,与一致预期 EPS 同口径;扣非口径并列作交叉)→ `qoq`(最新单季扣非,money=true,仅作拐点信号);EPS 序列(元/股)用 money=false | 最新报告期有扣非净利润;单季序列 ≥ 8 期;每个数带报告期与单位;每次拆分 / 求和都有 calculation_id |
| estimates | 一致预期 EPS(FY T / T+1 / T+2)+ 机构数 + 区间 | ★ fetch_estimates | `forward_cagr(eps_t = FY T 均值, eps_t_plus_n = FY T+2 均值, years=2)`;`consensus_dispersion(min, mean, max)` 对 FY T+2 | 机构数 ≥ 3(否则标"一致预期不可靠"并继续);min / max 必须一起报;走东财逐篇备源时只能叫"逐篇预测",不得冒充一致预期,且不得进 forward_cagr |
| valuation | 标准产出列 | ★ fetch_quote(总市值、现价)○ fetch_pe_history(分位) | `pe_deducted_annualized(总市值 evidence 的 value+unit, 最新单季扣非 value+unit;单位原样传入,由 calc 归一)`;`forward_pe(现价, FY T 均值 EPS)`;`pe_ttm_from_parts`(与数据源 pe_ttm 交叉);`percentile_rank(history={"history_csv": {"raw_ref": <PE 历史 raw>, "column": "peTTM", "where": {"tradestatus": "1"}}}, current = pe_ttm)`;`peg(扣非×4 PE, 前瞻 CAGR)`;`pe_digestion_scenarios(扣非×4 PE, 前瞻 CAGR)`(四个锚 30 / 25 / 22 / 18 各算);`forward_vs_ttm_judgement(前瞻 CAGR, 归母 TTM 同比)` | 标准产出列每一格要么有 calculation_id,要么写"未获取:原因";无意义域如实 not_meaningful;季节性提示(淡季单季×4 会高估 PE)写入推断段;PE 消化年数只基于当前 PE 与前瞻 CAGR,必须标注"CAGR 为预测";本产品不输出价格锚(红线) |
| risk | 反证与裁决点 | ○ fetch_announcements ○ fetch_kline | — | 每个强结论至少一条反证;前瞻 vs TTM 判读已给出并解释;一致预期分歧已报;数据源冲突逐条列出;数据缺口列出;至少三个裁决点(什么数据出来会推翻 + 下一个公开数据时点) |
| report | 按契约写 report.md | — | — | 结构 = 结论摘要 / 事实 / 推断 / 估值 / 风险与反证 / 裁决点 / 数据缺口;原始事实标 evidence id、派生数字标 calculation id;无任何投资动作建议;状态如实 |
**T 的定义**:T = 当前财年(Asia/Shanghai 当日所在年)。
**配套口径 skills(同目录,按阶段加载,与本表不冲突、只更细)**:financials → `earnings-analysis`(口径地图 / 三表交叉 / 比率经 calc `ratio`);valuation → `valuation`(三口径 PE / 前瞻 vs TTM / 四锚与 30 倍锚三铁律 / 判读);profile 与 risk 的产业链位置与不可替代性标签 → `industry-chain`;risk → `catalyst-risk`(催化剂分类 / 风险十类 / 裁决点写法)。
## 2. 依赖矩阵与阶段状态
阶段状态 ∈ `complete / incomplete / skipped / failed`;报告状态 ∈ `complete / incomplete / failed / stale`。
| 上游缺失 | 下游处理 |
|---|---|
| fetch_quote 失败(无现价 / 总市值) | valuation 全部 PE 类计算 skipped;报告 incomplete |
| 报价日期判定(所有情形先做;与下面 is_stale 分支的盘前例外保持一致:pre_open 下 quote_date ∈ {reference_quote_day, last_trading_day} 均视为正常) | 以 **`fetch_trade_calendar`** 的 `reference_quote_day`(盘前 = 上一交易日;其余 = 最近交易日)为准:`quote_date` == `reference_quote_day` → 日期正常(休市 / 盘前 / 盘后皆如此),报告写明报价日期与 `session_phase`;`quote_date` < `reference_quote_day` → 该股停牌或数据陈旧 → 按 stale 处理(个股 K 线日期不能单独作为"休市"依据——连续停牌的个股 K 线也会停在旧日);`quote_date` > `reference_quote_day`:仅当 `session_phase` == `pre_open` 且 `quote_date` == `last_trading_day`(集合竞价阶段报价已切到当日)→ 视为盘前正常,按昨收(last_close)继续;其他任何"未来日期"→ 数据异常,记 events 并按 incomplete 处理(不估值) |
| fetch_quote `is_stale=true`(成交额 0 且现价 == 昨收) | 若 `session_phase` == `pre_open` 且 `quote_date` ∈ {`reference_quote_day`, `last_trading_day`}(盘前报价停在上一交易日,或集合竞价已切到当日)→ 盘前正常现象,按昨收(last_close)报价继续(报告写明"盘前报价 = 上一交易日收盘");否则 profile Gate 不过、valuation 全部 PE 类计算 skipped、报告状态 = **stale**,结论摘要首条写明"行情为停牌 / 废码报价,估值不可用" |
| fetch_quote `is_stale=unknown`(备源缺昨收 / 成交额) | 二次验证:`quote_date` == `reference_quote_day` 且 `fetch_kline` 最新一根 K 线日期 == `reference_quote_day` 且该根成交量 > 0 → 按正常报价继续(events 记二次验证);否则按 `is_stale=true`(非盘前)处理 |
| fetch_financials 缺扣非(走了利润表备源) | 扣非×4 PE / PEG / 消化年数 skipped,写"未获取:扣非缺失";归母 TTM 同比仍算;报告 incomplete |
| fetch_estimates 失败或只有逐篇预测 | forward_cagr / forward_pe / PEG / 消化年数 / 判读 skipped;报告 incomplete |
| fetch_pe_history 失败或北交所 | 分位列写"未获取:原因";**不影响报告状态**(参考列) |
| fetch_announcements / fetch_kline 失败 | 风险阶段少一类线索,写入数据缺口;不影响状态 |
规则:上游关键输入缺失时,下游**不调用**对应 calc、不用旧值或记忆补;只生成结构化缺口。脚本退出码 2(partial)要读 `extra.degraded` 决定是否算"缺失"。
缺口(gaps)必须结构化:`{operation: <calc 函数名或脚本名>, reason_code: source_failed | source_partial | upstream_not_meaningful | upstream_missing | insufficient_periods | not_supported_market | optional_skipped | other, detail: 说明}`,编排器按 operation 精确匹配,不接受自由文本。
**报告状态优先级(多种情形同时命中时取最高)**:`failed`(关键脚本全部失败 / 无法产出 report.md)> `stale`(行情不可用于估值)> `incomplete`(关键数据缺口)> `complete`。stale 与 incomplete 同时命中 → 状态 stale,数据缺口仍逐条列出。
## 3. 每阶段结束的五问(逐条自问,答不上就回去补)
1. 这一步有没有心算或自己换算单位?(有 → 改走 calc,金额带单位)
2. 拉的字段里有没有能推翻结论的那一类?(只拉了支持面 → 补拉)
3. 来源 / 用途分清了?分子分母同期?
4. 转向看最新期间、规模看 TTM,报告期打印了?
5. 强结论找反证了?
## 4. 产物与落盘(契约见 AGENTS.md §4)
```
.local/runs/<run-id>/
manifest.json run_id / symbol / market / started_at / finished_at / status / stages[] /
codex_version / model / calc_version / repo_version / config_hash / raw_hashes(编排器扫描 raw/ 写入)
raw/ 脚本自动落盘的原始响应
fetch/ 每个取数脚本的结构化输出(<script>.json,脚本自动催化剂与风险的反证式写法:每个强结论必须先找反证;催化剂按"兑现型 / 预期型 / 周期型"分类并要求可验证的数据时点;风险按技术路线断层、客户集中、产能过剩与价格战、周期顶、预期透支(假便宜 PEG)、一致预期下修、治理与流动性、数据源冲突分类;裁决点的标准写法(什么数据出来会改变判断 + 下一个公开数据时点);知识档案旧结论的反证处理。当任务涉及风险、反证、催化剂、裁决点、预期兑现、什么会推翻结论时加载;单纯取数、估值计算、财报拆分等不需要反证框架的任务不要加载。只产出框架、概率与裁决点,不给投资动作建议。
A 股零鉴权取数手册。当需要真实的行情 / 市值 / 估值快照、季度报告期累计财务数据、机构一致预期 EPS、PE 历史序列、公告标题、日 K 线、交易日历时使用;只允许运行本 skill 登记的脚本取数(腾讯 / 新浪 / 同花顺 / baostock / 深交所 / 东财),禁止凭模型记忆给数,禁止自造爬虫。概念解释、观点讨论等不需要取数的话题不要加载。
财报拆解手册:报告期累计值 → 单季(quarterize)→ 最新单季 / TTM / TTM 同比 / 环比的口径地图,扣非与归母的取舍(一次性损益),季节性与报告期对齐(分子分母同期),三表交叉核对(利润表 / 资产负债表 / 现金流量表),比率(毛利率 / 费用率 / 负债率)一律经 calc ratio,"转向看最新期、规模看 TTM"的判读模板与质量检查清单。当任务涉及财报、季报、业绩、利润拆分、同比环比、毛利率、现金流、扣非时加载;只查行情 / 公告 / 产业链结构、或只讨论概念不涉及财务数字的任务不要加载。取数层不做任何算术,所有派生数字出自 calc;不给投资动作建议。
产业链下钻与不可替代性判定方法:以龙头为"需求入口"沿供应链逐层下钻(整机 / 龙头 → 部件 → 核心器件 → 材料 → 衬底与设备),用物理 / 材料约束(扩产周期、良率、认证周期、有无替代)当筛子找供给刚性的卡口;给每个标的贴不可替代性标签(tech_moat / capacity_moat / both / 待补)并列证据;含"卡口越硬越贵"与预期差四问的校准。当任务涉及产业链位置、上下游、护城河、不可替代性、供给瓶颈、竞争格局时加载;单纯取数、估值计算、财报拆分等不涉及产业链结构的任务不要加载。只产出框架与证据表,不给投资动作建议。
成长股估值口径手册(A 股为主,US/HK 通用):扣非×4 年化 PE、前瞻 PE、TTM PE 历史分位、PEG(扣非×4 PE ÷ 前瞻 CAGR)、前瞻 CAGR 与 TTM 同比交叉验证、一致预期分歧、四锚 PE 消化年数与"30 倍锚三铁律"、判读规则与常见错误。当任务涉及估值、PE、PEG、贵不贵、能不能消化、历史分位、一致预期时加载;只讨论概念、与估值无关的取数 / 行情 / 公告问题不要加载。所有数字一律经 calc/ 计算,本 skill 只管口径与判读,不给价格锚、不给投资动作建议。