说实话,我第一次接触DCF估值的时候,脑子里全是“现金流折现”这几个字在打转,但完全不知道从哪下手,后来我试着用Golang写了一个小工具,一边写一边理解,结果发现自己像是把财务课本里的公式变成了活的东西,那种感觉,怎么说呢,就像你亲手搭了个乐高模型,然后看着它自己动起来。
先别急着往下滑,咱们慢慢聊,DCF估值,全称是Discounted Cash Flow,也就是现金流折现法,听起来很唬人,其实核心逻辑就一句话:一家公司今天值多少钱,取决于它未来能产生多少现金,并且这些现金在今天值多少,只不过,这句话背后藏着大量的计算和假设,手算怕是要算到头皮发麻。
为什么选Golang?不是Python更香吗?
很多人问我,做这种财务模型,Python不是更合适吗?pandas、numpy一套下来多方便,这话没错,但我想说,Golang有它的独特优势。
Golang的并发模型特别适合做敏感性分析,你想想,DCF模型里假设条件一变(比如增长率从5%改成6%),整个估值结果就跟着抖,如果你要跑几百上千种情景,Python有时会卡得让你想砸键盘,Golang的goroutine轻轻松松就能扛住这种压力。
Golang编译后的可执行文件很小,扔到服务器上就跑,不用装一堆依赖,我见过不少财务分析师,为了跑一个模型要在本地配半天环境,最后放弃了,用Go写,编译完直接给同事发个二进制文件,双击就能用——这在职场里简直是杀手锏。
这不是说Python不好,如果你要做快速原型,Python无敌,但如果你要做一个可靠、高效、可分发的估值工具,Go真的挺香。
DCF估值模型的骨架,用代码怎么搭?
我们先捋一遍DCF的核心流程,不管用什么语言,步骤基本一样:
- 预测未来几年的自由现金流
- 计算终值(Terminal Value)
- 用折现率把所有未来现金流转成现值
- 累加得到企业价值,再调整得到股权价值
用Golang写,第一步就是定义数据结构,我一般这么干:
type DCFInput struct {
RevenueGrowthRate float64
OperatingMargin float64
TaxRate float64
DiscountRate float64
TerminalGrowthRate float64
ProjectionYears int
InitialFreeCashFlow float64
}
看到这里你可能会笑,这也太简单了吧?对,就是简单,Golang没有类的概念,struct就是一切,你可能会觉得少了点什么,但实际写起来,这种质朴反而让人专注。
接下来是预测逻辑,比如预测未来第N年的自由现金流:
func predictFCF(input DCFInput, year int) float64 {
growthFactor := math.Pow(1+input.RevenueGrowthRate, float64(year))
return input.InitialFreeCashFlow * growthFactor
}
这里我故意写的很朴素,连错误处理都没加。实际生产代码里,你一定要考虑边界情况,比如年份为负数、增长率为0这些,但咱们现在是在聊思路,对吧?
然后是折现,这是DCF的灵魂操作,未来第N年的100块,在折现率10%的情况下,今天只值100 / (1+10%)^N,用Golang写:
func discountedValue(fcf float64, discountRate float64, year int) float64 {
return fcf / math.Pow(1+discountRate, float64(year))
}
终值的计算稍微复杂一点,一般用永续增长模型:
func terminalValue(lastFCF float64, discountRate float64, growthRate float64) float64 {
return lastFCF * (1 + growthRate) / (discountRate - growthRate)
}
这里有个坑: 如果折现率小于增长率,这个公式就爆炸了,数学上分母变成负数,结果毫无意义,所以在写代码时,一定要加个判断:
if discountRate <= growthRate {
// 要么报错,要么用其他方法
return 0, fmt.Errorf("discount rate must be greater than terminal growth rate")
}
你看,写代码的过程,其实就是逼着你去理解公式背后的含义。如果你只是抄公式,你永远不会发现这个漏洞。

当你开始跑模型,就会发现“假设”才是最要命的
我用Go把模型搭好后,随便输了一组数据:初始自由现金流100万,增长率8%,折现率10%,折现年限10年,终值增长率3%,跑出来企业价值大概是1850万左右。
然后我手贱,把增长率从8%改成了9%,你猜怎么着?企业价值直接飙到了2100万,增长率只差了1%,价值差了250万,整整13.5%。
这就是DCF被人诟病的地方——它对假设太敏感了,折现率差0.5%,终值增长率差0.2%,结果可能天差地别。
所以我后来在工具里加了一个表格输出功能,把不同假设下的估值列出来,看起来一目了然:
| 增长率 | 折现率9% | 折现率10% | 折现率11% |
| 7% | 2150万 | 1720万 | 1410万 |
| 8% | 2580万 | 2050万 | 1670万 |
| 9% | 3120万 | 2480万 | 2000万 |
这个表格是用Golang的text/tabwriter包生成的,对齐得整整齐齐。说实话,当几百个结果在终端里一行行刷出来的时候,那种感觉特别踏实。
如何让你的Go代码更“耐造”?
写DCF模型的时候,浮点数精度是个头疼的问题,Golang的float64精度够用,但当你把几百万几千万的数字做乘除,小数点后面几位可能会有一点点偏差。不过说实话,对于估值这种本身就有很大不确定性的工作,这个误差可以忽略。
我强烈建议你把所有输入参数都做成配置文件,比如JSON或者YAML格式,这样你就不用每次改代码重新编译,用encoding/json包解析一下就好:
type Config struct {
Input DCFInput `json:"input"`
OutputFormat string `json:"output_format"`
}
这样,你的领导说“再跑一组折现率12%、增长率6%的情况”,你只需要改一下JSON文件,然后go run main.go——两秒钟的事。
让我坦诚说两句
其实DCF估值这个东西,从来不是算得越精确越好,因为未来本来就是不确定的,你算到小数点后八位,只能给自己一种虚假的安全感,真正有价值的是,通过这个模型,你开始思考:哪些假设对估值影响最大?那些关键假设靠谱吗?
用Golang写DCF模型,让我养成了一个习惯:每次跑完模型,我都会对着结果发一会儿呆,不是在看数字,而是在想背后的商业逻辑。代码只是手段,理解才是目的。
有时候我也会用Excel再算一遍对比一下,结果基本一致,但用Go的好处是,当我需要批量跑上千个情景时,Excel就卡死了,而Go的程序还在稳步推进。
好了,就写到这吧,你要是也想试试,可以先从最简单的现金流预测开始,别一上来就搞什么加权平均资本成本、债务调整啥的,慢慢地,你会发现这个工具越来越顺手,你对估值的理解也越来越深。说到底,估值这件事,比的是谁更能看清楚未来,而不是谁敲键盘更快。
本文来自作者[kyadmin]投稿,不代表思利达立场,如若转载,请注明出处:http://yl.c-lida.com/post/36.html
评论列表(4条)
我是思利达的签约作者“kyadmin”!
希望本篇文章《用Golang写一个DCF估值模型?这事我干过,还挺上瘾》能对你有所帮助!
本站[思利达]内容主要涵盖:郑州思利达智能科技有限公司
本文概览:说实话,我第一次接触DCF估值的时候,脑子里全是“现金流折现”这几个字在打转,但完全不知道从哪下手,后来我试着用Golang写了一个小工...