关于 company-valuation
company-valuation 是用于分析公司隐含股价的技能,通过 DCF、同业倍数及适用时的 SOTP 进行交叉估值,并提供敏感性和情景分析。
主要功能
- 01五年 FCFF 预测与 DCF
- 02同业倍数相对估值
- 03多业务分部公司的 SOTP
- 04WACC 与终值增长率敏感性分析
- 05看涨、基准与看跌情景
适用场景
- 01估算公司的隐含股价
- 02比较公司与同业的估值
- 03评估估值对假设变化的敏感度
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ himself65/ finance-skills --skill company-valuation安装后在 Claude Code 中输入/company-valuation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdreferences/dcf.mdrelative_valuation.mdsotp.mdwacc_erp_rates.md详细介绍
公司估值
如果你的宿主环境尚未执行以下 shell 探测命令(例如在 Codex 中),请先使用其 shell 工具运行 ! 包裹的命令,再选择设置路径。请使用实际输出;如果无法访问 shell,请说明这一限制。
通过三种方法交叉验证内在价值,再将结果融合为隐含股价:
- DCF — 预测 5 年 FCFF,按 WACC 折现,并计算终值。
- 相对估值 — 应用同行公司的 P/E、EV/Revenue、EV/EBITDA 中位数。
- SOTP — 存在 2 个或更多独立报告分部时,按各分部的纯业务同行倍数估值。
务必提供 WACC × 终值增长率敏感性表,以及看涨/基准/看跌情景。
免责声明:仅用于研究和教育,不构成财务建议。
步骤 1:检测流程
检测数据源和运行时依赖项。该技能支持 2 种方法路径——选择可用的最完整路径。
环境状态:
!`python3 -c "exec('try:\n import yfinance, numpy, pandas\n print(\'YFIN_OK\')\nexcept Exception:\n print(\'YFIN_MISSING\')')"`
!`python3 -c "exec('try:\n import yfinance as yf\n t=yf.Ticker(\'^TNX\')\n p=t.fast_info.last_price\n print(f\'RF_10Y={p/100:.4f}\')\nexcept Exception:\n print(\'RF_FETCH_FAIL\')')"`
决策树:
| 条件 | 方法路径 |
|---|---|
YFIN_OK | 路径 A(主要路径):使用 yfinance 获取财务数据和同行倍数 |
YFIN_MISSING | 路径 B:安装 yfinance,然后使用路径 A。python3 -m pip install -q yfinance numpy pandas |
RF_FETCH_FAIL | 使用默认值 rf = 0.045,并在输出中注明无风险利率可能已过时 |
如果输出了 RF_10Y=,请在步骤 4d 中使用该值作为 rf,而不是硬编码的 4.5%。
步骤 2:选择方法并设置默认值
方法适用性
| 公司类型 | DCF | 相对估值 | SOTP | 备用方案 |
|---|---|---|---|---|
| 成熟型现金流业务(CPG、电信、公用事业) | ✅ 主要方法 | ✅ | ❌ | — |
| 高增长 SaaS / 软件 | ✅ 谨慎使用 | ✅ 主要方法 | ❌ | 使用 EV/Revenue + Rule of 40 |
| 多分部企业集团 | ✅ | ✅ | ✅ 主要方法 | 参见 references/sotp.md |
| 银行 / 保险 | ❌ | ✅(P/B、P/TBV) | ❌ | DDM 或超额收益法;在输出中注明 |
| 尚未产生收入 | ❌ | 仅 EV/Revenue | ❌ | 标记为低置信度 |
| REITs | ❌ | ✅(P/FFO、P/AFFO) | ❌ | 基于 NAV |
| 周期性行业(能源、半导体、工业) | ✅ 基于中周期水平 | ✅ | 有时 | 按完整周期进行标准化 |
默认值表
提取数据前,先确定每个参数——除非用户另有指定,否则使用以下默认值。
| 参数 | 默认值 | 理由 |
|---|---|---|
| 预测期限 | 5 年 | 标准的明确预测期 |
终值增长率 g | 2.5% | 约等于美国长期 GDP 增速 |
无风险利率 rf | 步骤 1 中的实时 10Y UST,否则为 4.5% | 当前资本成本基准 |
股权风险溢价 erp | 5.5% | Damodaran 中间区间 |
| Beta | 来自 yfinance 的 info['beta'] | 市场观察到的杠杆 Beta |
债务成本 kd | interest_expense / total_debt,否则为 5.5% | 实际利率;备用值基于投资级债券利差 |
| 税率 | 过去 3 年实际税率的中位数,下限 15%,上限 30% | 剔除一次性因素的影响 |
| 利润率假设 | 各项比率过去 3 年的中位数 | 平滑周期性波动 |
| SBC 处理方式 | 软件/SaaS 视为现金成本;工业/CPG 视为非现金成本 | 行业惯例 |
| 同行数量 | 4-6 家 | 平衡信号与噪声 |
| 同行倍数 | 中位数(而非均值) | 降低异常值的影响 |
| 方法权重(不含 SOTP) | DCF 50% / 相对估值 50% | 等权交叉验证 |
| 方法权重(含 SOTP) | DCF 40% / 相对估值 30% / SOTP 30% | 适用时为 SOTP 分配权重 |
| 敏感性网格 | WACC ±1%,步长 0.5% × g 从 1.5-3.5%,步长 0.5% | 5×5 矩阵 |
当前无风险利率、ERP 表格和各行业 WACC 基准请参见 references/wacc_erp_rates.md。
步骤 3:获取数据
import yfinance as yf
import numpy as np
import pandas as pd
TICKER = "AAPL" # replace
t = yf.Ticker(TICKER)
info = t.info
income_a = t.income_stmt
cashflow_a = t.cashflow
balance_a = t.balance_sheet
income_q = t.quarterly_income_stmt
cashflow_q = t.quarterly_cashflow
earnings_est = t.earnings_estimate
revenue_est = t.revenue_estimate
price = info.get("currentPrice") or info.get("regularMarketPrice")
market_cap = info.get("marketCap")
shares_out = info.get("sharesOutstanding")
total_debt = info.get("totalDebt") or 0
cash = info.get("totalCash") or 0
beta = info.get("beta") or 1.0
sector = info.get("sector")
industry = info.get("industry")
关键财务报表项目(yfinance 标签):
| 需求 | 项目 |
|---|---|
| 营收 | Total Revenue |
| EBIT | Operating Income |
| 净利润 | Net Income |
| D&A | Depreciation And Amortization(在现金流量表中) |
| 资本支出 | Capital Expenditure(负值) |
| ΔNWC | Change In Working Capital(现金流量表) |
| SBC | Stock Based Compensation(现金流量表) |
第 4 步:构建 DCF
完整方法论和行业特定调整见 references/dcf.md。快速框架:
# 4a. Revenue growth path — fade from Y1 (consensus or hist CAGR) to terminal g
rev = income_a.loc["Total Revenue"].dropna().iloc[::-1].values # yfinance columns are newest-first; reverse to oldest -> newest
hist_cagr = (rev[-1] / rev[0]) ** (1 / (len(rev)-1)) - 1
y1 = float(revenue_est.loc["+1y", "growth"]) if "+1y" in revenue_est.index else hist_cagr
g_terminal = 0.025
growth_path = np.linspace(y1, g_terminal + 0.01, 5)
# 4b. Margins — 3y median
ebit_margin = float((income_a.loc["Operating Income"] / income_a.loc["Total Revenue"]).iloc[:3].median())
da_pct = float((cashflow_a.loc["Depreciation And Amortization"] / income_a.loc["Total Revenue"]).iloc[:3].median())
capex_pct = float((cashflow_a.loc["Capital Expenditure"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())
nwc_pct = float((cashflow_a.loc["Change In Working Capital"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())
eff_tax = (income_a.loc["Tax Provision"] / income_a.loc["Pretax Income"]).iloc[:3].median()
tax_rate = float(min(0.30, max(0.15, eff_tax))) if pd.notna(eff_tax) else 0.21 # 3y median effective, floored 15%, capped 30%
# 4c. FCFF per year
rev_t = [float(income_a.loc["Total Revenue"].iloc[0])]
fcff = []
for g in growth_path:
rev_t.append(rev_t[-1] * (1 + g))
ebit = rev_t[-1] * ebit_margin
nopat = ebit * (1 - tax_rate)
fcff.append(nopat + rev_t[-1]*da_pct - rev_t[-1]*capex_pct - rev_t[-1]*nwc_pct)
# 4d. WACC
rf, erp, kd = 0.045, 0.055, 0.055 # override rf with live value from Step 1
ke = rf + beta * erp
e_v = market_cap / (market_cap + total_debt)
d_v = 1 - e_v
wacc = e_v*ke + d_v*kd*(1 - tax_rate)
# 4e. Terminal value — compute both, use midpoint
tv_gordon = fcff[-1] * (1 + g_terminal) / (wacc - g_terminal)
tv_exit = (rev_t[-1] * ebit_margin + rev_t[-1] * da_pct) * 15 # peer median EV/EBITDA
tv_base = 0.5 * (tv_gordon + tv_exit)
# 4f. Bridge to equity
pv_fcff = sum(f / (1+wacc)**(i+1) for i, f in enumerate(fcff))
pv_tv = tv_base / (1+wacc)**5
ev = pv_fcff + pv_tv
equity = ev + cash - total_debt
implied_price_dcf = equity / shares_out
检查项:(a)如果 wacc <= g_terminal → 停止,终值增长率过于激进;(b)如果 pv_tv / ev > 0.85 或 < 0.45 → 标记并展示两种终值计算方法;(c)如果 wacc 超出 references/wacc_erp_rates.md 中的行业合理区间 → 说明。
第 5 步:相对估值
选择 4-6 家可比公司。可比公司映射和调整规则见 references/relative_valuation.md。
PEERS = ["MSFT", "ORCL", "CRM", "NOW", "SAP", "WDAY"] # pick by industry
multiples = {}
for p in PEERS:
pi = yf.Ticker(p).info
multiples[p] = {
"pe_fwd": pi.get("forwardPE"),
"ev_rev": pi.get("enterpriseToRevenue"),
"ev_ebitda": pi.get("enterpriseToEbitda"),
"ps": pi.get("priceToSalesTrailing12Months"),
}
med_pe = np.nanmedian([v["pe_fwd"] for v in multiples.values()])
med_ev_rev = np.nanmedian([v["ev_rev"] for v in multiples.values()])
med_ev_eb = np.nanmedian([v["ev_ebitda"] for v in multiples.values()])
eps_ttm = float(income_q.loc["Diluted EPS"].iloc[:4].sum())
rev_ttm = float(income_q.loc["Total Revenue"].iloc[:4].sum())
ebitda_ttm = float(income_q.loc["EBIT"].iloc[:4].sum()) + float(cashflow_q.loc["Depreciation And Amortization"].iloc[:4].sum())
net_debt = total_debt - cash
implied_pe = med_pe * eps_ttm
implied_ev_rev = (med_ev_rev * rev_ttm - net_debt) / shares_out
implied_ev_ebit = (med_ev_eb * ebitda_ttm - net_debt) / shares_out
implied_price_rel = np.nanmedian([implied_pe, implied_ev_rev, implied_ev_ebit])
如果目标公司的增长或利润率特征存在重大差异,可比公司中位数上调或下调 10-30%。务必说明调整幅度及理由。SaaS 的 Rule of 40 基准见 references/relative_valuation.md。
第 6 步:分部加总估值(SOTP,仅适用于多分部公司)
除非 10-K 报告中列出 2 个或更多经济特征不同的经营分部,否则跳过。yfinance 不会提供分部数据,用户必须自行提供或从申报文件中解析。完整方法论见 references/sotp.md:
- 确定各分部及其对应的纯业务可比公司
- 应用可比公司 EV/EBITDA 中位数(增长型分部使用 EV/Rev)
- 扣除未分摊的公司成本(未知时按收入的 2-5% 计,上限为 5%)
- 扣除净债务和少数股东权益;再除以股份数
SOTP 折价 =(SOTP 价格 − 市场价格)/ SOTP 价格。如果 >20%,则标记为企业集团折价。
第 7 步:交叉验证、敏感性分析和情景分析
# Blended implied price
if sotp_price is None:
blended = 0.5*implied_price_dcf + 0.5*implied_price_rel
else:
blended = 0.4*implied_price_dcf + 0.3*implied_price_rel + 0.3*sotp_price
# 5x5 sensitivity grid
wacc_grid = [wacc + dx for dx in (-0.01, -0.005, 0, 0.005, 0.01)]
g_grid = [0.015, 0.020, 0.025, 0.030, 0.035]
sens = {}
for w in wacc_grid:
for g in g_grid:
tv = fcff[-1]*(1+g)/(w-g)
pv = sum(f/(1+w)**(i+1) for i,f in enumerate(fcff)) + tv/(1+w)**5
sens[(w,g)] = (pv + cash - total_debt) / shares_out
此外,还要给出 Bull / Base / Bear 情景:收入增长率调整 ±300 个基点,EBIT 利润率调整 ±200 个基点,WACC 调整 ∓100 个基点,终值增长率分别设为 3.0% / 2.5% / 1.5%。
第 8 步:回复用户
按以下顺序呈现估值结果:
- 核心结论 — 用一句话说明加权公允价值、当前价格、上涨或下跌百分比,以及最看多或最看空的方法。
- 概览 — 行业板块、行业、总市值、当前价格、3 个月 / 12 个月价格变动、LTM 收入增长率。
- 三种方法汇总 — 3 列表格:方法 | 隐含价格 | 权重 | 简要理由。
- DCF 构建 — 假设表(增长路径、利润率、WACC 组成、终值方法)+ 5 年 FCFF 预测表 + EV 到股权价值的桥接表。
- 可比公司对照 — 可比公司表格,包含预期 P/E、EV/Rev、EV/EBITDA、毛利率、收入增长率;最后一行为中位数;标记目标公司的溢价 / 折价。
- SOTP(如适用)— 分部表格 + 调整项 + 股权价值。
- 敏感性矩阵 — WACC × g 网格(5×5),突出显示基准情景。
- 情景分析 — Bull / Base / Bear 情景表,列出关键变量和隐含价格。
- 主要风险 — 说明哪些假设对估值影响最大,以及哪些因素可能推翻投资逻辑。
错误处理
| 缺失情况 / 边界情况 | 处理方式 |
|---|---|
yfinance 返回 None 作为 beta | 使用 references/wacc_erp_rates.md 中的行业默认 beta |
| LTM EBITDA 为负 | 跳过 EV/EBITDA 倍数;采用 EV/Revenue + DCF |
| LTM EPS 为负 | 跳过 P/E 倍数;如果预期 P/E 为正则使用,否则跳过 |
| Gordon 模型中的增长率 > WACC | 将 g = wacc − 0.5% 设为上限并标记 |
| 历史数据不足 3 年 | 使用现有数据;将数据置信度标记为“低” |
| 获取可比公司数据失败 | 从中位数计算中剔除该公司;在输出中说明 |
| SOTP 没有分部数据 | 跳过第 6 节;仅采用 DCF + 相对估值 |
需包含的注意事项
- TTM 数据存在滞后;可比公司倍数反映市场情绪(可能过度波动)
- DCF 结果取决于输入数据的质量;敏感性分析比单点估值更重要
- yfinance 数据未经官方认证;做出任何决策前都应与一手申报文件交叉核对
- 这不是财务建议
参考文件
references/dcf.md— DCF 方法论和行业特定指引(软件、零售、金融、医疗保健、能源、制造业、CPG、电信、REIT、流媒体)references/relative_valuation.md— 可比公司筛选、倍数调整规则、Rule of 40、按主题划分的可比公司组references/sotp.md— 分部加总估值方法论、企业集团折价识别、催化因素references/wacc_erp_rates.md— 无风险利率、股权风险溢价、行业 WACC 基准、行业默认 beta