请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3365818 新帖:25

开源工具系列-脱胎于巴菲特的股票估值分析工具

特朗普对头发表于:5 月 10 日 05:20回复(1)

开源工具系列-脱胎于巴菲特的股票估值分析工具

此文的算法脱胎于《巴菲特之道 》

author: aaron-clark-aic
这货的其他文章目录

多年以来,金融分析师们使用过很多公式计算一个公司的内在价值。一些人喜欢快速简单的方法:低市盈率、低市净率和高分红率。但根据巴菲特的观点,最好的系统描述是约翰·伯尔·威廉斯70年前的定义,在《投资价值理论》一书中,他的定义是:“一个公司的价值决定于在其生存期间,预期产生的所有现金流,在一个合理的利率上的折现。”巴菲特解释说:“这样的话,无论是马鞭制造商,还是手机运营商(尽管性质各异),所有企业在经济层面都是一样的。”

巴菲特指出这个方法的使用,非常类似于债券的估值过程。债券通常具有明确的票面利息和到期日,人们可以清楚地知道,它在到期日之前产生的现金流。如果你将所有收到的票息加在一起,然后除以一个合适合适的贴现率,就会得到债券的当前价格。要确定一个企业的价值,分析者只要估计公司未来的股东盈余现金流,并用合适的利率进行贴现,即可得到其现在的价值(现值)

好,上面一段是我承认在巴菲特的书里抄的!那么这么多一大段东西能不能通过人话翻译出来?答案是肯定的!

上面的内容说的最主要的就是两点:1、估算企业价值 2、合理折现率

翻译成人话我们要通过一个数学公式估算出企业的价值,而且这个价值能够通过合理折现率变成钱。

“我擦,这TM谁能够算得出来,算得出来都去开巴菲盛宴了”

但是,还真的有人能算的出来这个公式,谁啊?当然是天天和钱打交道的银行了。我是长期没有存款的人~,不知道你们有没有,所以我不知道现在的存款利率,但是我有房啊~我很清楚现在银行商业贷款需要我交多少利息!我买房的时候为什么银行会贷款给我?当然是为了赚钱,也就是说,银行将现金折算成价值以及这个价值应该产生多少现金才划算,也就是起码差不多也许大概要跑赢果丹皮(GDP)吧!我这里姑且将这个利率定为6%:

 def __init__(self,standardRate = 0.06):
        self.__standardRate__ = standardRate

现在折现率有了,不过房子贷的款是确定的,企业呢?一个企业到底能变成多少等价的钱,莎士比亚说“这是一个问题!”。不过巴菲特说,这不是一个问题,企业估价看“股东盈余现金流”。也就是巴菲特认为,股东盈余才是真真能够代表股东们投入的钱,而这些钱是需要生钱的!

“龙生龙凤生凤,耗子生儿打地洞!”,钱当然是要生钱的。银行说,少了6个点,我们不贷款给你们买房~。股东们当然也会说,少了6个点我还不如把钱借给aaron-clark-aic买房~,开什么企业,开企业。

现在明显了,我们只要计算出一个企业的股东盈余,再除以,利率。这个就是企业的估值?是的,好像巴菲特说的就是这个。

=/使 

现在解决几个问题:

  1. 什么是“股东盈余”,google,bing随便选一个~
    = ?
  1. 什么是“资本支出”?翻墙选google~
    =
  1. 什么是“净经营性资产增加”?继续翻墙~
    =?
  1. 什么是“折旧与摊销”? 恩,这个不用管,直接用代数消元法~

=? /

巴菲的的公式计算好了,我们只需要知道净利润,固定资产的变化值,房贷利率,就能够知道现在的股价对应的企业到底是贵了还是便宜了。

下面是关于代码的一些说明:

1.这个类用于平滑股价,为什么平滑股价请参考我的文章

class SmoothPrice:
    #code

2.这个类用于生成查询季度,为什么用季度来查询请参考我的文章

class SeasonReports:
    def __init__(self):
        self.__today__ = datetime.date.today()
        self.__toyear__ = datetime.datetime(self.__today__.year,1,1)
        self.__lastyear__ = datetime.datetime((self.__toyear__ - datetime.timedelta(days=1)).year,1,1)
        self.__lasttwoyear__ = datetime.datetime((self.__lastyear__ - datetime.timedelta(days=1)).year,1,1)

    def seasonDaily(self):
        season = ['q4','q3','q2','q1']
        # print([str(self.__toyear__.year) i for i in season] [str(self.__lastyear__.year) i for i in season] [str(self.__lasttwoyear__.year) i for i in season])
        _out = [str(self.__toyear__.year) i for i in season] [str(self.__lastyear__.year) i for i in season] [str(self.__lasttwoyear__.year) i for i in season]
        _out.reverse()
        return _out

3.生成图像中的绿线,黄线,红线分别是什么意思:

绿色实线:最近4个月的季报汇总,且以总股数计算

绿色虚线:同绿色实线,且以流通股数计算

黄色实线:上一次计算(也是按季报汇总),也就是往前追溯一个季度的计算值,且以总股数计算

黄色虚线:同黄色实线,且以流通股数计算

红色实线:上上次计算,也就是往前追溯两个季度的计算值,且以总股数计算

红色虚线:同红色实线,且以流通股数计算

import numpy as np
import datetime
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import math
# 平滑价格
# @inP [,,,,] 价格数组
class SmoothPrice:

    def __init__(self,inP:np.ndarray):
        self.__inP__ = inP
        self.__l_smooth__ = inP
        self.__q_smooth__ = inP
        self.__c_smooth__ = inP

    def __linearSmooth5__(self):
        _outP = []
        inP = self.__inP__
        n = len (inP)
        if n < 5:
            for i in inP:
                _outP.append (i)
        else:
            _outP.append ((3.0 * inP[0] + 2.0 * inP[1] + inP[2] - inP[4]) / 5.0)
            _outP.append ((4.0 * inP[0] + 3.0 * inP[1] + 2 * inP[2] + inP[3]) / 10.0)
            _i = 0
            for i in inP:
                if not _i == 0 and not _i == 1 and not _i == n - 2 and not _i == n - 1:
                    _outP.append ((inP[_i - 2] + inP[_i - 1] + inP[_i] + inP[_i + 1] + inP[_i + 2]) / 5.0)
                _i = _i + 1
            _outP.append ((4.0 * inP[n - 1] + 3.0 * inP[n - 2] + 2 * inP[n - 3] + inP[n - 4]) / 10.0)
            _outP.append ((3.0 * inP[n - 1] + 2.0 * inP[n - 2] + inP[n - 3] - inP[n - 5]) / 5.0)
        self.__l_smooth__ = _outP

    def __quadraticSmooth5__(self):
        _outP = []
        inP = self.__l_smooth__
        n = len (inP)
        if n < 5:
            for i in inP:
                _outP.append (i)
        else:
            _outP.append ((31.0 * inP[0] + 9.0 * inP[1] - 3.0 * inP[2] - 5.0 * inP[3] + 3.0 * inP[4]) / 35.0)
            _outP.append ((9.0 * inP[0] + 13.0 * inP[1] + 12 * inP[2] + 6.0 * inP[3] - 5.0 * inP[4]) / 35.0)
            _i = 0
            for i in inP:
                if not _i == 0 and not _i == 1 and not _i == n - 2 and not _i == n - 1:
                    _outP.append ((- 3.0 * (inP[_i - 2] + inP[_i + 2]) + 12.0 * (inP[_i - 1] + inP[_i + 1]) + 17 * inP[
                        _i]) / 35.0)
                _i = _i + 1
            _outP.append (
                (9.0 * inP[n - 1] + 13.0 * inP[n - 2] + 12.0 * inP[n - 3] + 6.0 * inP[n - 4] - 5.0 * inP[n - 5]) / 35.0)
            _outP.append (
                (31.0 * inP[n - 1] + 9.0 * inP[n - 2] - 3.0 * inP[n - 3] - 5.0 * inP[n - 4] + 3.0 * inP[n - 5]) / 35.0)
        self.__q_smooth__ = _outP

    def __cubicSmooth5__(self):
        _outP = []
        inP = self.__q_smooth__
        n = len (inP)
        if n < 5:
            for i in inP:
                _outP.append (i)
        else:
            _outP.append ((69.0 * inP[0] + 4.0 * inP[1] - 6.0 * inP[2] + 4.0 * inP[3] - inP[4]) / 70.0)
            _outP.append ((2.0 * inP[0] + 27.0 * inP[1] + 12.0 * inP[2] - 8.0 * inP[3] + 2.0 * inP[4]) / 35.0)
            _i = 0
            for i in inP:
                if not _i == 0 and not _i == 1 and not _i == n - 2 and not _i == n - 1:
                    _outP.append ((-3.0 * (inP[_i - 2] + inP[_i + 2]) + 12.0 * (inP[_i - 1] + inP[_i + 1]) + 17.0 * inP[
                        _i]) / 35.0)
                _i = _i + 1
            _outP.append (
                (2.0 * inP[n - 5] - 8.0 * inP[n - 4] + 12.0 * inP[n - 3] + 27.0 * inP[n - 2] + 2.0 * inP[n - 1]) / 35.0)
            _outP.append (
                (- inP[n - 5] + 4.0 * inP[n - 4] - 6.0 * inP[n - 3] + 4.0 * inP[n - 2] + 69.0 * inP[n - 1]) / 70.0)
        self.__c_smooth__ = _outP

    def smooth(self):
        self.__linearSmooth5__()
        self.__quadraticSmooth5__()
        self.__cubicSmooth5__()
        return self.__c_smooth__

class SeasonReports:
    def __init__(self):
        self.__today__ = datetime.date.today()
        self.__toyear__ = datetime.datetime(self.__today__.year,1,1)
        self.__lastyear__ = datetime.datetime((self.__toyear__ - datetime.timedelta(days=1)).year,1,1)
        self.__lasttwoyear__ = datetime.datetime((self.__lastyear__ - datetime.timedelta(days=1)).year,1,1)

    def seasonDaily(self):
        season = ['q4','q3','q2','q1']
        # print([str(self.__toyear__.year)+i for i in season]+[str(self.__lastyear__.year)+i for i in season]+[str(self.__lasttwoyear__.year)+i for i in season])
        _out = [str(self.__toyear__.year)+i for i in season]+[str(self.__lastyear__.year)+i for i in season]+[str(self.__lasttwoyear__.year)+i for i in season]
        _out.reverse()
        return _out
    
#处理股票代码
class Stocks:

    def combineIdustryStocks(self,industries:list):
        return  np.hstack([ get_industry_stocks(i)  for i in industries])

    def standardStocks(self,stockList):
        _stocks = list(get_all_securities('stock').index)
        _standardStocks = []
        for i in stockList:
            _standardStocks =_standardStocks+[code for code in _stocks if code.upper().startswith(i)]
        return _standardStocks
    
class TargetStock:

    def __init__(self,standardRate = 0.06):
        self.__standardRate__ = standardRate

    def setSeasonReport(self,seasonDaily,stock):
        list =[]
        # print(seasonDaily)
        for _season in seasonDaily:
            jqv = valuation
            df = get_fundamentals(query(jqv.code,jqv.circulating_cap,jqv.capitalization,income.net_profit,jqv.market_cap,balance.fixed_assets,indicator.eps
                                              ).filter(jqv.code == stock
                                                       ).order_by(
                                                                  ),statDate = _season
                                    )
            if not df.empty:
                list.append([
                    ('net_profit',df['net_profit'][0]),
                    ('circulating_cap',df['circulating_cap'][0]),
                    ('capitalization',df['capitalization'][0]),
                    ('seasonReport',_season),
                    ('fixed_assets'+_season,df['fixed_assets'][0]),
                    ('eps',df['eps'][0])
                    ])
        return list

    def getstockPrice(self,stock,days = 200,end_date = datetime.date.today(),frequency = 'daily'):
        _points = get_price(stock,count=days,end_date= end_date,frequency=frequency,fields=['close','volume'],fq=None)
        _in = _points['close'].values[:days]
        _in_volume = _points['volume'].values[:days]
        sp_in = SmoothPrice(_in)
        sp_in_volume = SmoothPrice(_in_volume)
        y_in = sp_in.smooth()
        volume_in = sp_in_volume.smooth()
        y_base = _in
        x=range(days)
        sr = SeasonReports()
        stock_sel = self.setSeasonReport(sr.seasonDaily(),stock)
        if len(stock_sel) < 6:
            print('warning,-'+stock+'-seasons is not enough!')
            return False
        stock_sel.reverse()
        last_net_profit =sum([i[0][1] for i in stock_sel[1:4]])
        # print([i[0][1] for i in stock_sel[:4]])
        pre_net_profit = sum([i[0][1] for i in stock_sel[2:5]])
        base_net_profit = sum([i[0][1] for i in stock_sel[3:6]])
        # print(stock,[last_net_profit,pre_net_profit,base_net_profit])
        def fixed_assert(i):
            return stock_sel[i][4][1]
        def circulating_cap(i):
            return 10000*stock_sel[i][1][1]
        def capitalization(i):
            return 10000*stock_sel[i][2][1]
        last_target_price_circap = ((last_net_profit - fixed_assert(0)+fixed_assert(3))/self.__standardRate__)/circulating_cap(0)
        last_target_price_cap = ((last_net_profit - fixed_assert(0)+fixed_assert(3))/self.__standardRate__)/capitalization(0)
        pre_target_price_circap = ((pre_net_profit - fixed_assert(1)+fixed_assert(4))/self.__standardRate__)/circulating_cap(1)
        pre_target_price_cap = ((pre_net_profit - fixed_assert(1)+fixed_assert(4))/self.__standardRate__)/capitalization(1)
        base_target_price_circap = ((base_net_profit - fixed_assert(2)+fixed_assert(5))/self.__standardRate__)/circulating_cap(2)
        base_target_price_cap = ((base_net_profit - fixed_assert(2)+fixed_assert(5))/self.__standardRate__)/capitalization(2)
        # print([not math.isnan(i) for i in [last_target_price_circap,last_target_price_cap,pre_target_price_circap,pre_target_price_cap,base_target_price_circap,base_target_price_cap]])
        for i in [math.isnan(i) for i in [last_target_price_circap,last_target_price_cap,pre_target_price_circap,pre_target_price_cap,base_target_price_circap,base_target_price_cap]]:
            if i :
                print('warning,-'+stock+'-can\'t find enough circap or cap')
                return False
        # print(stock,[(last_target_price_circap,last_target_price_cap),
        #                 (pre_target_price_circap,pre_target_price_cap),
        #                 (base_target_price_circap,base_target_price_cap)
        #                ])
        # print([i[3][1] for i in stock_sel])
        return [('smoothPrice',y_in),('truthPrice',y_base),('x_axis',x),(
            'report',[('last_target_price_circap',int(last_target_price_circap)),
                      ('last_target_price_cap',int(last_target_price_cap)),
                      ('pre_target_price_circap',int(pre_target_price_circap)),
                      ('pre_target_price_cap',int(pre_target_price_cap)),
                      ('base_target_price_circap',int(base_target_price_circap)),
                      ('base_target_price_cap',int(base_target_price_cap))
                      ]),
                ('in_volume',volume_in),
                ('stock',stock),
                ('reportSeason',[i[3][1] for i in stock_sel])]

    def plotFigure(self,getstockPrice):
        if   getstockPrice == False or len(getstockPrice) != 7:
            print('warning empty')
            return False
        stock = getstockPrice[5][1]
        reportSeason = getstockPrice[6][-1][0]
        smoothPrice = getstockPrice[0]
        truthPrice = getstockPrice[1]
        x_axis = getstockPrice[2]
        report = getstockPrice[3]
        in_volume = getstockPrice[4]

        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
        fig = plt.figure (tight_layout=False,figsize=(13, 30))
        gs = gridspec.GridSpec (2, 2)


        ax = fig.add_subplot (gs[0, :])
        ax.plot (x_axis[1], smoothPrice[1], "b")
        ax.plot (x_axis[1], truthPrice[1], "y")
        # ax.set_ylabel ('YLabel0')
        ax.set_xlabel ('最近预估g(-[总股份]/--[流通股份]),上一季度起点y,上上季度起点r')
        ax.axhline ((report[1][0][1]), color='g', linestyle='--', label='last_target_price_circap')
        ax.axhline ((report[1][1][1]), color='g', linestyle='-', label='last_target_price_cap')
        ax.axhline (report[1][2][1], color='y', linestyle='--', label='pre_target_price_circap')
        ax.axhline (report[1][3][1], color='y', linestyle='-', label='pre_target_price_cap')
        ax.axhline (report[1][4][1], color='r', linestyle='--', label='base_target_price_circap')
        ax.axhline (report[1][5][1], color='r', linestyle='-', label='base_target_price_cap')
        ax.grid ()

        ax = fig.add_subplot (gs[1, :])
        ax.set_ylabel ('量  能')
        ax.plot (x_axis[1], in_volume[1], 'g')
        ax.grid () 
      
        plt.show()
_st = Stocks()
_sd = SeasonReports()
_as = TargetStock()
_as.plotFigure(_as.getstockPrice(stock=_st.standardStocks(stockList=['000915'])[0]))
 

全部回复

0/140

量化课程

    移动端课程