浅谈估值模型:实现GGM的理想国(附代码)

2023-12-17 00:08

本文主要是介绍浅谈估值模型:实现GGM的理想国(附代码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、写在前面

二、缘起

三、描绘一个现实中的理想世界

1:预期收益率r

2:下一期股利与增长率g

3:永续增长率g

4:内在价值计算

四、模型结果        

五、写在后面


一、写在前面

1:本文主要讨论戈登增长模型的优化及现实意义;另外,本文使用python实现模型并以某证券为例进行估值

2:本文主要行情数据通过Tushare(ID:444829)金融大数据平台接口获取, 部分财务指标,利率指标通过爬虫获取

3:笔者希望搭建出一套交易体系,原则是只做干货的分享。后续将更新更多模块,但工作学习之余的闲暇时间有限,更新速度慢还请谅解

4:文中假设与观点是基于笔者对模型及数据的一孔之见,若有不同见解欢迎随时留言交流

5:模型实现基于python3.8

二、缘起

        先来段开场白,笔者认为一些理念性的东西价值远超过代码和模型本身,不过对长篇大论不感兴趣的小伙伴们可以直接跳过,从第三部分开始看~

        戈登增长模型(Gordon Growth Model, GGM) 笔者常戏称之为哥哥们。 戈登增长模型其实是股息贴现模型(DDM)的一种优化,通过计算公司预期未来支付给股东的股利现值,来确定股票的内在价值,即是股利永续流入。GGM在上世纪五十年代被John Burr Williams发布出来,而它的弟弟——DDM则早在上世纪三十年度被提出。事实上,那个时代对股票的理念便是买入并持有以赚取分红。现如今近百年过去,这样的理念早被急不可耐的"行为金融学大师们"抛弃;尤其在A股,市场上的投资者多为买入并持有以赚取差价。

        巴菲特说过:“如果没有持有一支股票十年的打算就不要买入”。老人家的底气其实可以用这样的公式解释:站在今天看未来可以获得的好处,如果超过今天所付出的对价那便赚了。

马化腾暗笑:对对对,你赚了_马化腾_暗笑表情

        在笔者眼中这是比薛定谔的猫还要高维度的存在——这个盒子里明天有没有猫。作为一个唯物主义无神论者,我甚至没有能力知道现在盒子里是否有猫,更遑论未来股价是多少? 事实是小到小散们的怒掷一手的交易,大到分析师报告里的纸上谈兵(  买!),只要不是有能力画K线的的主力,或是内幕的知(lao)情(shu)人(cang),我们都在拼命的引入未来函数。

        什么?巴菲特?他老人家也引入了,不然20年疫情抄底航空股一抄抄在半山腰?

 

        (本文只论基本面,老缠师请不要与我论道)佛陀有言:“一切有为法,皆待缘而起”。世间的结果都是因各种因缘而成,即为缘起股价和股利亦是如此,笔者认为大可不必视未来函数如洪水猛兽,但其信号漂移是一定需要考虑的,我们所能做的就是加上限定条件,例如给我一只半透明的盒子或是一杆秤,那么可以通过观察盒子或是称重量间接知道盒子里是不是有猫。

         事实情况却远比薛定谔的猫来得复杂:首先它站在空间维度(今天会不会有猫);对一个企业来说,目前所处行业,财务水平,管理层质量等等判断企业(盒子)会不会大概率产生持续的利润(猫)。其次是站在时间维度(明天会不会有猫)导致各种信号产生的漂移,这是痛点所在,股利是否能永续流入?企业能否以目前状况永续经营?外部黑天鹅事件,外部冲击et al. 万事皆可谓“缘起” 。

        第二种情况较为复杂,出于内容规划考虑,笔者想在后续模块中寻求更好的解法。

        第一种情况较为简单,但由于N-holding period 太过苛刻,对权益工具来说难以实现。本文就从相对现实一些的GGM切入。

三、描绘一个现实中的理想世界

        先为新世界导入必要的模块

import pandas as pd
import tushare as ts
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import seaborn as sns

       P=\frac{D_{1}}{r-g} 

        为了求出股价必须先求出:

1:预期收益率r

2:下一期股利与增长率g

3:永续增长率g


        为保证模型有效性,建议采用10年的数据,5年也可。接下来以某市场指数, 及某证券自2011年1月1日起,至2021年9月7日所有交易日数据为例进行演示(为避免荐股嫌疑就不展示具体代码了,下面相关信息也都会隐去)。以下结果均是通过row data计算得到,自己计算各种指标的最大的好处就是将一切问题可控,且可以进行很强的个性化处理。

1:预期收益率r

        r 的求法有很多,这里通过CAPM实现,即

 r = r_{f}+\beta (r_{m}-r_{f})

        先来个可爱的迷你爬虫获取最新的国债利率做为无风险收益率rf,如果嫌麻烦也可以做好模型直接导入具体数值。table存的是国债收益率表,三年到十年都有,这里取三个月短期国债收益率做为无风险收益率,即table.loc[1,2]。

def risk_free(Date): ## Date: year-month-dayurl = "https://yield.chinabond.com.cn/cbweb-cbrc-web/cbrc/historyQuery?startDate=" + Date+ "&endDate=" + Date+ "&gjqx=0&qxId=ycqx&locale=cn_ZH&mark=1 "content = pd.read_html(url)table = content[1]print('Risk_free rate(Short term (3 month) T-Bill):', table.loc[1, 2], "%")return table.loc[1, 2]

        接着是市场回报r_{m}及敏感系数beta 

        这里引入一款非常方便的数据获取模块tushare,通过简洁的数据接口可以获取各类金融数据,省去大把写爬虫的时间。通过这个链接Tushare大数据社区注册成功后即可通过自己的token便捷调用数据,具体可参考Tushare的技术文档。

        只需要对pro.daily传入证券代码及起止日期即可获取所有日线级别数据,并如法炮制获取市场指数数据。

def date_input(token, cp, mk,  start, end):pro = ts.pro_api(token)cp = pro.daily(ts_code=cp, start_date=start, end_date=end)         #company数据mk = pro.index_daily(ts_code=mk, start_date=start, end_date=end)   #market数据beta = beta_value(cp, mk) # 计算beatMr = mean_value(mk)       # 计算市场回报率

        使用sklearn对日回报进行拟合,这里对没有数据的停牌交易日进行剔除以保证指数与个股数据是成对出现。

def beta_value(cp, mk):for i in mk['trade_date'].values:if i not in cp['trade_date'].values:# Match the market data-set with securities, some securities may haltmk = mk[~mk['trade_date'].isin([i])] # 剔除个股停牌日return_cp = cp ["pct_chg"] return_mk = mk ["pct_chg"]return_cp = return_cp.reshape(-1, 1)return_cp = return_cp[::-1] return_mk = return_mk.reshape(-1, 1)return_mk = return_mk[::-1]Mod = LinearRegression() Mod.fit(return_mk, return_cp) # 采用线性回归对市场及个股数据进行拟合beta = Mod.coef_[0][0]        # 计算出相关系数,即βprint("Beta is", beta)# print("R_square是", Model.score(return_mk, return_cp))return beta

        计算得该证券

        既然得到了十多年的数据,手痒顺便看下中心趋势及分布状况是什么样的

def dis_function(cp, mk):m, s = pd.Series(mk["pct_chg"]), pd.Series(cp["pct_chg"])print("market mean:",mk["pct_chg"].mean(),"market median:",mk["pct_chg"].median())print("security mean", cp["pct_chg"].mean(), "security median:",cp["pct_chg"].median())print("market skewness:",m.skew(),"market kurtosis:",m.kurt())print("security skewness:",s.skew(),"security kurtosis:",s.kurt())dataset = pd.DataFrame({"market": mk["pct_chg"].values, "security":             cp["pct_chg"].values})sns.jointplot(x= "security", y = "market", data = dataset, kind = "reg")plt.show()

        可得: 

        近似是个正态,关于分布笔者会在后续的文章中用到,这里仅作展示。

        接着把指数的日涨跌幅均值计算出来并年化处理,这里按365天算了,如果只算交易日就是250天左右:

def mean_value(mk):mean = mk['pct_chg'].mean()mean_yield = ((1 + mean / 100) ** 365 - 1) * 100print("Average return of the Market(Suggest 10 years):", mean_yield, "%")return mean_yield

计算得

        至此,我们便得到了所有计算回报率 r 的要件,调用之前的模块并计算r为:

rf = float(risk_free()) # Calculate risk free rate
beta_Mr = data_input(company, market, start, end)
beta = beta_Mr[0] # Beat value
Mr = beta_Mr[1]
expt_r = rf + beta * (Mr - rf)

计算可得E(r): 5.1745 %

2:下一期股利与增长率g

        第一个迷之信仰来了:现在没有分红,如何知道下一期股利?结合增长率g来看大概有几种解法:

        1):公式法

        通过ROE*(1-Payout Ratio)求出增长率g,这个增长率即是假设公司的利润率不变,盈利效率不变,财务政策不变的情况下,可以实现的增长率。于是再使用当期股利乘以这个增长率以计算出下一期股利 。

        2):历史数据法

        通过过去推未来,计算过去一段时间内的股利年化增长率,再用当期股利乘以这个增长率以计算出下期股利

        3):预测(夸下海口)法

        由以上两种方式计算出g。根据分析师灵(pou)敏(gai)的职业判断对计算的g进行调整并向投资者夸下海口。

        如果结合内幕消息或是撞大运正好猜中,我们可以得到第四种解法:

        4):什么都不干,直接把已知的下一期股利放入公式。

        可以看到以上三种方式都用到了增长率g的概念,后面会再对g进行讨论,接下来先以公式法为例进行计算。

        首先来个精致的爬虫从新浪上获取历史股利信息:

def craw_dividend(company): #start, end,url = "http://vip.stock.finance.sina.com.cn/corp/go.php/vISSUE_ShareBonus/stockid/" \+ str(company.split(".")[0]) + ".phtml"table = pd.read_html(url, attrs={"id": "sharebonus_1"}, header=[2])div_inform = pd.DataFrame(table[0][::-1])

        print出表格一看,什。。什么!一年竟然分好几次红公司和不分红的铁公鸡?处理一下表格,不分红为0,分红多次的求其合计数做为该年股利。

# 接上一个代码块
year = []
div = []
div_hist = div_inform["派息(税前)(元)"]
for i in div_inform["公告日期"]:row = div_inform[(div_inform["公告日期"] == i)].index.tolist()div.append(float(div_hist[row].values))year.append(i.split("-")[0])
dataset = {"year": year, "dividend": div}
div_table = pd.DataFrame(data=dataset)  # 只包含分红信息和年份的表格rows_list = []
for i in div_table["year"]:index = div_table[(div_table["year"] == i)].index.tolist()rows_list.append(div_table[(div_table["year"] == i)].index.tolist()) if index not in rows_list else next
for rows in rows_list:if len(rows) == 1:continueelse:total_div = 0for row in range(1, len(rows)):total_div = total_div + div_table["dividend"][rows[row]]div_table.drop(rows[row], inplace=True)div_table.loc[rows[0], "dividend"] = total_div + div_table["dividend"][rows[0]]
div_table.reset_index(drop=None, inplace=True)
print(div_table)
return div_table, div_inform  ## divd_inform has all div. information,div_table only has【year】and 【div】

        这里留了个口子把div_inform 也取了出来,方便以后写程序可以直接调用。

        得到div_table如下:

         感兴趣的读者可以通过历史数据法计算推出下一期股利,这里采用公式法只取出当期股利。

3:永续增长率g

        通过ROE*(1-Payout Ratio)求出增长率g

        继续一个蠢萌的迷你爬虫获取ROE:

def craw_roe(company):url 
="http://money.finance.sina.com.cn/corp/go.php/vFD_FinancialGuideLine/stockid/"+str(company.split(".")[0])+"/ctrl/2019/displaytype/4.phtml"ratio = pd.read_html(url, attrs={"id":"BalanceSheetNewTable0"})return ratio

        接着通过Tushare接口获取相关财务指标以计算g:

def tus_growth(ts_code,period):global growthperiod_beg = str(int(period) -1) +"1231"period = period+"1231"ratio_end = pro.query('fina_indicator', ts_code=ts_code , period=period)gl_table= pro.income(ts_code=ts_code, period=period, fields='ts_code,n_income', report_type=1)ratio_beg = pro.query('fina_indicator', ts_code=ts_code , period=period_beg)roe = float(ratio_end["roe"][0])                 # ROEre_beg =float(ratio_beg["retained_earnings"][0]) # Retain earning at the beg of periodre_end= float(ratio_end["retained_earnings"][0]) # Retain earning at the end of periodn_income = float(gl_table["n_income"][0])        # Net incomegrowth = roe *(1-(re_end-re_beg)/ n_income)print("Growth rate of",ts_code,"is",growth,"%","with ROE:",roe, "  net income:",n_income," at the period ""end of", str(period)[:4])return growth

 计算得:

        结合之前股利历史状况,1.3%的增速也可以理解。。可以视为几乎没有增速。净利润和后面的日期这些信息就不展示了。

        接下来,万事具备只欠东风!

 4:内在价值计算

         调用之前的模块,传入市场代码,证券代码,起止日期,股利计算日期,大功告成!

if __name__ == '__main__':market = input('Market Code')  # 000001.SHcompany = input('Security Code')  # 600522.SHstart = input('Start Date(Dealing Day)')end = input('End Date(Dealing Day)')#Clac risk free , beta & E(r)rf = float(risk_free()) # Calculate risk free ratebeta_Mr = data_input(company, market, start, end)beta = beta_Mr[0] # Beat valueMr = beta_Mr[1]expt_r = rf + beta * (Mr - rf)print("Output", company, "E(r):", expt_r, "%")
# calc dividend at beginningdiv_table = craw_dividend(company)[0]div_date = input("Input a complete financial period (year) for dividend")div_date = str(div_date[:4])row = div_table[(div_table["year"] == div_date)].index.tolist()div_value = div_table.iloc[int(row[0])]["dividend"]print("The dividend for {0}".format(div_date),"is:", div_value)# calc gdiv_date= int(div_date)-1 ## still need adjustmenttus_growth(company, str(div_date))# Formular(Golden growth model)price = div_value*(1+(growth/100)) / ((expt_r - (growth))/100)print("The intrinsic value of", company,"is", price)

      四、模型结果        

        该证券模型结果为:

        笔者对该结果颇为满意,笔者利用相对估值法计算也有着相似的价值。不管模型是不是真正估计到了这只明天盒子里薛定谔的猫,在笔者心里已经成功了。

五、写在后面

        程序可以个性化查看,但也可以批量跑,把Input的信息变成列表便利即可把大A四千家公司统统撸一遍。

        笔者所用来展示代码的公司是经过挑选的,换句话说只有符合条件的公司才能用GGM折现。这基于以下几点条件或假设:

        1):该公司增长率极低,因此股利折现时g的威力对模型没有什么影响;但对于一些高分红,高ROE的公司来说,该模型或面临失效;2):模型结果是该公司在笔者眼中的价值,该公司经营基本面稳定,且未来长期永续经营,笔者也进行长期投资;3):这家公司有足够多的数据(至少近十年)供计算,对于一些上市时间较短的公司,模型有效性或将大打折扣。

        更现实情况是许多公司分红都是不稳定或呈现周期性波动,这样可以将过去几年的股利均值视为D_{0}进行计算。增长率g也并非永续且固定,多阶段折现,采用更保守的方式折现都可以估计出一个区间,这个区间下限将是保守值,上线将是预警值。

        最后,笔者认为决定一个公司股价高度的是市值而并非其内在价值,内在价值只能决定一个均值回归时的中枢,以内在价值进行交易是不一定划算的。 

这篇关于浅谈估值模型:实现GGM的理想国(附代码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/502401

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了