DCF 模型构建器 是做什么的?
概述
该技能用于创建符合投资银行标准的机构级 DCF 模型,以进行股权估值。每次分析都会生成一个详细的 Excel 模型(敏感性分析包含在 DCF 工作表的底部)。
工具
- 默认使用用户提供的所有信息以及可用于数据获取的 MCP 服务器。
关键约束 - 请先阅读
以下约束适用于所有 DCF 模型构建过程。开始前请审阅:
环境:Office JS vs Python/openpyxl:
- 如果在 Excel 内运行(Office 加载项 / Office JS 环境): 直接使用 Office JS — 不要使用 Python/openpyxl。通过
range.formulas = [["=D19*(1+$B$8)"]]编写公式。无需单独的重新计算步骤;Excel 原生计算。使用range.format.*进行样式设置。同样的公式优先规则适用:对派生单元格设置.formulas,切勿设置.values。 - 如果生成独立的 .xlsx 文件(无实时 Excel 会话): 按如下所述使用 Python/openpyxl,然后在交付前运行
recalc.py。 - 本技能的其余部分使用 openpyxl 示例 — 在该环境中时转换为 Office JS API 调用,但所有原则(公式字符串、单元格批注、区域检查点、敏感性表格循环)同样适用。
⚠️ Office JS 合并单元格陷阱: 当构建使用合并单元格的区域标题时,不要调用 .merge() 然后在合并区域上设置 .values——Office JS 仍会报告区域的原始尺寸,并抛出 InvalidArgument: The number of rows or columns in the input array doesn't match the size or dimensions of the range。相反,应先将值写入左上角单元格,然后合并并格式化整个区域:
// 错误 — 会抛出 InvalidArgument:
const hdr = ws.getRange("A7:H7");
hdr.merge();
hdr.values = [["市场数据与关键输入"]]; // 1×1 数组 vs 1×8 区域 → 失败
// 正确 — 先在单个单元格上设置值,然后合并并格式化区域:
ws.getRange("A7").values = [["市场数据与关键输入"]];
const hdr = ws.getRange("A7:H7");
hdr.merge();
hdr.format.fill.color = "#1F4E79";
hdr.format.font.bold = true;
hdr.format.font.color = "#FFFFFF";
这适用于 DCF 中的每个合并区域标题(市场数据、情景块、现金流预测、终值、估值摘要、敏感性表)。
公式优先于硬编码(不可商议):
- 每个预测、利润率、折现因子、现值以及敏感性单元格必须是实时 Excel 公式——绝不是在 Python 中计算后写为数字的值
- 使用 openpyxl 时:
ws["D20"] = "=D19*(1+$B$8)"是正确的;ws["D20"] = calculated_revenue是错误的 - 允许的唯一硬编码数字是:(1) 原始历史输入,(2) 假设驱动因子(增长率、WACC 输入、终值增长率 g),(3) 当前市场数据(股价、债务余额)
- 如果你发现自己在 Python 中计算某些内容并将结果写入——停下。模型必须在用户更改假设时灵活调整。
与用户分步验证(不要端到端构建):
- 数据检索后 → 向用户显示原始输入块(收入、利润率、股份数、净债务),并在预测前确认
- 收入预测后 → 显示预测的营收线和增长率,在构建利润率之前确认
- 自由现金流构建后 → 显示完整的自由现金流时间表,在计算 WACC 前确认逻辑
- WACC 后 → 显示计算过程和输入,在折现前确认
- 终值加现值后 → 显示权益桥(企业价值 → 权益价值 → 每股价值),在敏感性表前确认
- 在每个阶段捕获错误——错误的利润率假设在敏感性表构建后被发现意味着重建所有下游内容
敏感性表:
- 使用奇数的行数和列数(标准:5×5,有时 7×7)——这保证有一个真正的中心单元格
- 中心单元格 = 基础案例。 构建轴值,使中间行标题和中间列标题恰好等于模型的实际假设(例如,如果基础 WACC = 9.0%,中间行为 9.0%;如果终值 g = 3.0%,中间列为 3.0%)。因此中心单元格的输出必须等于模型实际的隐含股价——这是检查表格构建正确的合理性检查。
- 高亮中心单元格,使用中蓝色填充 (
#BDD7EE) 加粗字体,使其立即可见哪个单元格是基础案例。 - 填充所有单元格(通常 3 个表 × 25 个单元格 = 75 个)包含完整的 DCF 重新计算公式
- 使用 openpyxl 循环(或 Office JS 循环)以编程方式编写公式
- 无占位符文本,无线性近似,无手动步骤要求
- 每个单元格必须针对该假设组合重新计算完整 DCF
单元格批注:
- 在创建每个硬编码值时立即添加单元格批注
- 格式:“来源:[系统/文档],[日期],[引用],[URL 如果适用]”
- 每个蓝色输入在进入下一部分前必须有一个批注
- 不要推迟到最后或写“待办:添加来源”
模型布局规划:
- 在编写任何公式前先定义所有区域的行位置
- 先编写所有标题和标签
- 其次编写所有区域分隔线和空白行
- 然后使用固定的行位置编写公式
- 在创建后立即测试公式
公式重新计算:
- 在交付前运行
python recalc.py model.xlsx 30 - 修复所有错误,直到状态为“success”
- 零公式错误要求(#REF!、#DIV/0!、#VALUE! 等)
情景块:
- 为熊市/基础/牛市情况创建单独的块
- 在每个块内将假设水平显示在预测年份上
- 使用 IF 公式:
=IF($B$6=1,[熊市单元格],IF($B$6=2,[基础单元格],[牛市单元格])) - 验证公式是否引用了正确的情景块单元格
DCF 流程工作流
第 1 步:数据检索与验证
从 MCP 服务器、用户提供的数据和网络获取数据。
数据源优先级:
- MCP 服务器(如果配置)——来自 Daloopa 等提供商的结构化财务数据
- 用户提供的数据——来自其研究的财务历史数据
- 网络搜索/获取——需要时的当前价格、Beta、债务和现金
验证检查清单:
- 验证净债务与净现金(对估值至关重要)
- 确认摊薄后的流通股数(检查最近的股票回购/发行)
- 验证历史利润率是否与业务模型一致
- 交叉检查收入增长率与行业基准
- 验证税率是否合理(通常为 21-28%)
第 2 步:历史分析(3 - 5 年)
分析并记录:
- 收入增长趋势:计算 CAGR,确定驱动因素
- 利润率进展:跟踪毛利率、EBIT 利润率、FCF 利润率
- 资本强度:折旧摊销和资本支出占收入的百分比
- 营运资本效率:净运营资本变化占收入增长的百分比
- 回报指标:ROIC、ROE 趋势
创建汇总表,显示:
历史指标(LTM):
收入:X 百万美元
收入增长:X% CAGR
毛利率:X%
EBIT 利润率:X%
折旧摊销占收入的百分比:X%
资本支出占收入的百分比:X%
FCF 利润率:X%
第 3 步:构建收入预测
方法论:
- 从最新的实际收入开始(LTM 或最近财年)
- 对每个预测年份应用增长率
- 同时显示美元金额和计算的增长率百分比
增长率框架:
- 第 1-2 年:较高增长,反映近期可见性
- 第 3-4 年:逐渐放缓至行业平均水平
- 第 5 年以上:接近终值增长率
公式结构:
- 收入(Y