反向DCF怎么做:从当前股价求出需要实现的增长假设
反向DCF要回答的是:其他输入固定时,什么增长假设能让这套现金流模型算出指定股价。可靠的交付不是一个孤立百分比,而是一组可以复查的条件,包括现金流起点、折现率、终值、净债务、股数、允许搜索的范围和代回误差。它反映某个模型需要什么,不等于直接读出了所有投资者的真实预期。
本文用一家虚构公司演示,金额单位统一为百万元人民币,股数单位为百万股。只反求前三年企业自由现金流的年增长率,永续增长率另行固定。下载程序是离线独立计算,既不获取实时股价,也不代表CoTrading已有反向DCF界面或完整建模能力。
先冻结能正向算通的模型
准备经过核对的现金流模型,取得现金流基期、显式预测期间、折现率、终值方法、净债务和股数。把来源页码、估值日期、币种和证券类别写在输入表旁。只有一项股价还不够:如果报价对应另一股类,或者股数没有反映增发,求解器可以非常准确地算出一个毫无可比性的结果。
Damodaran的企业估值原始教材说明了FCFF按资本成本折现的方法。这里直接使用外供模型已经确定的自由现金流,不从收入或净利润自动推导现金流,也不把利润增长率直接改名为FCFF增长率。若原模型需要额外再投资才能增长,必须在模型内部先处理一致性,再进行反求。
本例固定基期FCFF为100,WACC为10%,永续增长率为2%,净债务120,股数100,目标股价14.55元。唯一可变化参数是前三年FCFF增长率h。预先批准的范围为−5%至25%,股价代回误差不得超过0.00000001元,最多迭代100次。范围、精度和预算都是研究约定,不能在看到答案后悄悄改动。
把正向方程和有效域写清楚
第t年的FCFF等于100 * (1+h)**t,各年按1.10**t折现。第三年末终值等于FCFF3 * 1.02 / (0.10 - 0.02),再折现三年。显式现金流现值加终值现值得到企业价值,减净债务后得到股权价值,最后除以股数。中间过程保持精度,只在展示摘要时四舍五入,避免逐层取整改变待求方程。
此模型要求起始FCFF和股数为正,h大于−100%,WACC大于非负的永续增长率。所有数字先转Decimal,再用有限数检查拒绝NaN、正无穷与负无穷。Python Decimal官方说明列出了这些特殊数值;能解析成Decimal不等于能当有效财务输入。完整样例对所有模型字段、目标价和区间端点都执行这道检查。
对这个特定模型可以说明单调性:h上升时,每一期正现金流都增加,固定折现分母为正,终值系数也为正。减去固定债务,再除以正股数,不改变递增关系,因此整个有效区间连续且严格递增。若换成含分段税率、负基期现金流或增长相关融资成本的工作表,这个证明就不能直接照搬,必须重新分析。
先判定是否包住目标,再二分
先计算两个端点对应的股价。如果14.55不在它们之间,就返回no_solution_in_domain并保留两端股价。不要为了让屏幕出现答案自动扩大范围。在已经证明连续严格递增的模型中,目标位于端点之间意味着域内只有一个解;目标恰好等于端点时也可以接受。本实现可能返回残差容差内接近端点的近似值,使用普通成功状态,不另附边界标签。
SciPy二分法文档说明了连续性和区间包根的要求。本文程序仅使用Python标准库自行实现有上限的二分循环,不需要安装SciPy。这里借助官方文档核对数值方法,并不意味着使用了该库,也不能由数值收敛进一步推导经济假设一定合理。
每轮取区间中点,计算模型股价减目标股价的残差。残差为负就提高下界,为正就降低上界。日志记录轮次、测试增长率和残差,误差达到约定才返回已求解。若100轮仍没满足,状态必须是未收敛,不能把循环自然结束时最后一个中点包装成成功。把失败状态与结果数值一起交付,便于下游阻断使用。
完整核对14.55元主例
h为10%时,前三年现金流分别为110、121和133.1,折现后均为100,显式现值合计300。第三年末终值1697.025,折现后为1275。企业价值因此是1575,扣除净债务120后股权价值1455,除以100百万股,得到每股14.55元。这些数量单位相互抵消,不能再额外乘一百万。
求解返回接近0.10的增长率,代回价差在规定范围以内。本例区间第一个中点正好就是0.10,所以很快收敛;这不代表可以删除迭代循环,换一个目标价仍需要逐轮缩小区间。程序既断言已知10%正向得到14.55,也断言反求增长与代回误差,而不是只核对字符串状态。
mid = (lo + hi) / 2
computed, terminal_share = value(mid, model)
residual = computed - target
if abs(residual) <= D("0.00000001"):
# 完整程序在此连同迭代日志返回结果。
pass
下载完整示例。使用Python 3.12+、标准库和合成数据,保存后运行python R546.py。上面只摘出停止条件,下载文件包含模型、完整循环、所有输入守卫和反例断言。
用两种不同机制的反例验收
第一种只把目标价改为99元,其余条件保持。该价格超出批准范围可达的价格区间,因此返回域内无解。这不是说股票永远无法估值,而是说现有条件无法同时满足。先核对股价、股数、净债务和模型,再决定是否有理由单独批准另一搜索范围;即使重跑,也保留这次无解记录,不能把旧输入覆盖掉。
第二种是非单调函数(h-0.05)**2,在相同区间先降后升。样例通过探测发现非单调并拒绝声称唯一解。有限网格可以发现反证,却无法证明任意回调在网格间也单调。若复杂模型可能有多个根,应拆成有依据的单调片段,或明确输出多个候选解和各自条件,不能让二分法偶然找到的一个根代表全部情况。
把终值依赖纳入结果解释
本例终值现值1275占企业价值1575的约80.95%。把它和增长率并列展示,读者才知道答案很依赖固定的2%永续增长和10%折现率。改变其中任何一项,就变成另一个反向DCF问题。可以另做有标签的敏感性研究,但不能同时放开多个输入让求解器随意凑到股价,再把其中某个参数当唯一隐含预期。
交付包应包含冻结输入表、方程版本、搜索区间、两端股价、逐轮日志、最终残差和终值占比。复查者应把返回增长率独立代入正向方程,在不相信求解日志的情况下仍能重现股价。收到新报价、现金流模型变更、债务调整或股数变更时,创建新版本重跑,旧版本继续保留其原来的日期和条件。
实际研究还要检查求出的增长是否符合经营约束。例如现金流增长需要新增产能、周转资金或资本开支时,应先把这些条件写回正向模型,再重新求解。不能因为程序输出10%就把它直接解释为收入也要增长10%。逆向计算的价值在于暴露需要进一步研究的条件,而不是代替经营判断或给出买卖结论。
对报价与模型边界再做一次交叉检查
记录目标价究竟是收盘价、最近成交还是另一个人工审核报价,并保留时间。模型能匹配某个参考报价,不表示那就是可执行成交价。报价时刻与模型会计截止可以不同,但要同时可见;若在某次资本变更前后跨日拼接,必须解释股数与价格是否已经调整一致,不能让求解器替研究者决定应选哪个版本。
发现增长率异常时,优先排查金额、股数、净债务符号和现金流定义。百万元与元混用会改变整条方程,减少求解容差不能修复单位错误。净债务若已经扣除了现金,不能又把同一现金重复加回。每次修正记录哪项输入改变、原答案为何无效,再以新版本重跑,不把更合理的结果当成旧运行自然正确的证据。
还可以让复查者只拿冻结表和正向函数,不看反求答案,独立确认端点是否覆盖目标、10%是否复现14.55,以及终值占比是否来自企业价值而非股权价值。三类检查分别针对区间、算术与解释,不应互相替代。最终结论写成带条件的完整句,让被固定的折现和终值假设跟随增长率一起进入后续研究材料。
常见问题
反求结果就是市场一致预期吗?
不是,它只是在这组固定参数下匹配股价的增长率。其他折现率、终值和现金流路径也可能匹配相同股价。
能直接套到负现金流公司吗?
本例拒绝这种扩展。需要先建立合适的正向模型,确认有效搜索片段,再选择求根方式。