[Programming] DCF free cash flow discount model-implementation based on python

Introduction : This is a DCF free cash flow discount model, which is an implementation of the content of Mr. Guo Yongqing’s book "Financial Statement Analysis and Stock Valuation"

Insert image description here

Precautions

  1. The code is based on "Financial Statement Analysis and Stock Valuation". The caliber of free cash flow is slightly different from the public perception. It is recommended to read Chapters 14 and 15 of the book before use;
  2. I am not a computer major, so there may be some errors in the model code;
  3. Banking stocks cannot be valued for the time being because their financial reporting format is slightly different from other types of companies;
  4. The data uses historical daily stock data and Sina financial data compiled by Mr. Xing Buhang ;
  5. I am not a finance or accounting major. Although I consulted CPA experts during the process, there may still be problems with the caliber of financial data.

Introduction to DCF

The free cash flow discount method is a type of absolute valuation method. The theoretical basis is the present value principle: the value of any asset is equal to the sum of the present values ​​of all its expected future cash flows, which for the company is free cash flow.

Since it is impossible to accurately predict all future free cash flows and stocks do not have a fixed life cycle, the model is simplified into the following four types:

Zero growth model: V = FCFWACC Constant growth model: V = FCF ( 1 + g ) WACC − g Two-stage model: V = ∑ t = 1 n FCF t ( 1 + WACC ) t + TV ( 1 + WACC ) n , Where TV = FCF n ( 1 + g 2 ) WACC − g 2 three-stage model: V = ∑ t = 1 n FCF 0 ( 1 + g 1 ) ( 1 + WACC ) t + ∑ t = n + 1 m FCF n ( 1 + g 2 ) ( 1 + WACC ) t + FCF n + m ( 1 + g 3 ) ( WACC − g 3 ) ( 1 + WACC ) n + m \begin{aligned} & Zero-growth model: V=\frac {FCF}{WACC}\\ &Constant growth model: V=\frac{FCF(1+g)}{WACC-g}\\ &Two-stage model: V=\sum_{t=1}^n\ frac{ {FCF}_t}{(1+WACC)^t}+\frac{TV}{(1+WACC)^n}, \ \ where TV=\frac{FCF_n(1+g_2)}{WACC- g_2}\\ & Three-stage model: V=\sum_{t=1}^n\frac{ {FCF}_0(1+g_1)}{(1+WACC)^t}+\sum_{t=n+ 1}^m\frac{ {FCF}_n(1+g_2)}{(1+WACC)^t}+\frac{FCF_{n+m}(1+g_3)}{(WACC-g_3)(1 +WACC)^{n+m}}\\ \end{aligned}Zero growth model : V=WACCFCFConstant growth model : V=WACCgFCF(1+g)Two- stage model : V=t=1n(1+WACC)tFCFt+(1+WACC)nTV, where T V  =WACCg2FCFn(1+g2)Three- stage model : V=t=1n(1+WACC)tFCF0(1+g1)+t=n+1m(1+WACC)tFCFn(1+g2)+(WACCg3)(1+WACC)n+mFCFn+m(1+g3)

  • The zero-growth model is suitable for mature and stable companies with no growth, and the annual free cash flow remains at a stable level, similar to a perpetual annuity; if all the free cash flow of such a company is used to pay dividends, then its The results are very close to the dividend discount model.
  • The constant growth model is suitable for mature companies where future free cash flow grows at a very slow rate.
  • In the two-stage model , investors' expected return WACC must be at least higher than the overall economic growth rate; the constant growth rate g2 is usually smaller than WACC. On the contrary, it means that the company's size will exceed the overall economic size in a long time.
  • In the three-stage model , it is assumed that all companies go through three stages: growth, transition, and stability. The growth rates in the three stages range from high to low, and the stable stage maintains constant growth at a lower growth rate.

Specific calculation steps:

  1. Calculate free cash flow and discount it according to the corresponding method ( ⋆ ⋆ ⋆ ⋆ ⋆ \star\star\star\star\star)
  2. Calculate equity value = discounted free cash flow + financial assets + long-term equity investment - company debt
  3. Calculate minority shareholder ratio
  4. Value attributable to shareholders of listed companies = Equity value × \times× (1-minority shareholder ratio)
  5. Intrinsic value per share = value attributable to shareholders of a listed company/equity

in,

  • Free cash flow from operating assets = incremental cash inflow on the premise that the company maintains its original production and operation scale = net cash flow from operating activities - preservation capital expenditures = net cash flow from operating activities - depreciation of fixed assets - intangible assets and long-term deferred payments Amortization of expenses - losses on disposal of long-lived assets
  • W A C C = k d × D D + E × ( 1 − t ) + k e × E D + E WACC=k_d\times\frac{D}{D+E}\times(1-t)+k_e\times\frac{E}{D+E} WACC=kd×D+ED×(1t)+ke×D+EE. Among them, the debt capital cost rate = total debt capital / average amount of debt capital $ \times $ 100% = (financial expenses + exchange income) / (beginning debt capital + ending debt capital) / 2; the equity capital cost rate should be higher than the national debt in the same period The interest rate, plus the risk premium of stock investment, is generally set to 9%; t is the company’s actual income tax rate = 1-net profit/pre-tax profit.
  • Corporate debt = interest-bearing debt
  • Minority shareholders ratio = minority shareholders’ equity Total shareholders’ equity\frac{Minority shareholders’ equity}{Total shareholders’ equity}Total shareholders' equityMinority interests _
  • Equity = market value/share price

how to use

  • You can directly copy the code at the end and use it;
  • You can enter the github website to download the code (ipynb format);
  • A copy of Inspur Information data is provided, located on the github website.

Variable description

#=== 变量
file_name = 'sz000977'  # 股票代码
time = 4  # 采用最近n期的数据(季)
zero_change = False  # 是否为零增长模型
one_change = False  # 是否为不变增长模型
two_change = True   # 是否为两阶段模型
three_change = False  # 是否为三阶段模型
g1, g2, g3 = 0.2, 0.03, 0.01  # 增长速度,分别是后三个模型的g,如果使用不变增长模型,则不需要更改后两个
t1, t2 = np.arange(1, 3), np.arange(1,2)  # 某阶段的几年,两阶段与三阶段模型需要,注意最大值减一为实际值

The above parts are content that can be modified, that is, modifying the above parts is enough to run.

file_nameIt refers to the stock code, that is, the file name for reading data. This program reads two files, daily stock data and financial data. Please be careful to change the read_fileaddress in the function to the address of your own computer when using it.

timeIt refers to the latest n-quarter data. Since financial data is divided into quarters, and the net cash flow in quarterly reports and semi-annual reports is prone to negative numbers, the default value is 4. The fourth quarter from the last quarter of the existing data is used, that is, the 2019 annual report. The data you fill in is different depending on your data.

changeThere are four models in the series. Write True after which model you need to use . Remember to write False if you don’t need it .

gThe series is the growth rate. Under the constant growth model, it is g1. Under the two-stage model, g1 is the predictable period, g2 is the constant growth rate. Under the three-stage model, g1, g2, and g3 are the growth rates in the growth, transition, and stable stages respectively. .

tThe series represents the specific number of years of the expected time. For example, under the two-stage model, t1 represents the number of years in the predictable period. Under the three-stage model, t1 and t2 represent the number of years in the growth and transition periods respectively; in addition, np.arangethe number generated by the function is smaller than the second value, such as np.arange( 1,3) generates [1, 2], np.arange(1,5) generates [1, 2, 3, 4]

The final output is as follows:

WACC is  0.07472217542810221
归属于上市公司股东的价值: 39579757869.00408 
 股票内在价值: 30.69977988736238
原PE:  48.261587273819586 估值PE: 49.223259346212316
前0个季度股票价格 30.39
前1个季度股票价格 39.18
前2个季度股票价格 38.78
前3个季度股票价格 30.1
前4个季度股票价格 25.7
前5个季度股票价格 25.02
前6个季度股票价格 26.96

code

import pandas as pd
import numpy as np

#=== 变量
file_name = 'sz000977'  # 股票代码
time = 4  # 采用最近n期的数据(季)
zero_change = False  # 是否为零增长模型
one_change = False  # 是否为不变增长模型
two_change = True   # 是否为两阶段模型
three_change = False  # 是否为三阶段模型
g1, g2, g3 = 0.2, 0.03, 0.01  # 增长速度,分别是后三个模型的g,如果使用不变增长模型,则不需要更改后两个
t1, t2 = np.arange(1, 3), np.arange(1,2)  # 某阶段的几年,两阶段与三阶段模型需要,注意最大值减一为实际值



#=== functions
def read_file(file_name):
    # 读取股票基本数据
    df = pd.read_csv(r'your_address\%s.csv' % file_name, encoding='GBK', skiprows=1, parse_dates=['交易日期'])
    df = df[['股票代码', '股票名称', '交易日期', '总市值', '净利润TTM', '收盘价']]
    print(df.tail(5))
    # 读取股票财务数据
    finance_df = pd.read_csv(r'your_address\%s.csv' % file_name, parse_dates=['财报日期', '财报发布日期'], skiprows=1, encoding='gbk')
    finance_df = finance_df.resample('Q', on='财报日期').first()
    del finance_df['财报日期']
    finance_df.reset_index(inplace=True)
    finance_df.dropna(subset=['财报发布日期'], inplace=True)
    finance_df.sort_values(by='财报发布日期', inplace=True)
    return df, finance_df


def merge_data(df, finance_df):
    add_columns = ['B_货币资金',  
     'B_交易性金融资产',
     'B_衍生金融资产',
     'B_应收票据及应收账款',
     'B_应收票据',
     'B_应收账款',
     'B_应收款项融资',
     'B_应收利息',
     'B_应收股利',
     'B_其他应收款',
     'B_买入返售金融资产',
     'B_发放贷款及垫款',
     'B_可供出售金融资产',
     'B_持有至到期投资',
     'B_长期应收款',
     'B_长期股权投资',
     'B_投资性房地产',
     'B_所有者权益(或股东权益)合计',   
     'C_经营活动产生的现金流量净额', 
     'B_短期借款',
     'B_交易性金融负债',
     'B_应付利息',
     'B_应付短期债券',
     'B_一年内到期的非流动负债',
     'B_长期借款',
     'B_应付债券',
     'B_租赁负债',
     'B_长期应付款(合计)',
     'R_财务费用',
     'R_汇兑收益',
     'R_四、利润总额',
     'R_减:所得税费用',
     'C_固定资产折旧、油气资产折耗、生产性物资折旧', 'C_无形资产摊销', 'C_长期待摊费用摊销', 'C_处置固定资产、无形资产和其他长期资产的损失',
     'B_少数股东权益']
    col = ['财报发布日期', '财报日期'] + add_columns
    stock_df = pd.merge_asof(df, finance_df[col], left_on='交易日期', right_on='财报日期', direction='backward')
    print(stock_df.columns)
    return stock_df


def data_been_prepared(now_df, stock_df):
    now_df[['股票代码', '股票名称', '交易日期', '总市值', '财报发布日期', '财报日期', '净利润TTM', '收盘价']] = stock_df[['股票代码', '股票名称', '交易日期', '总市值', '财报发布日期', '财报日期', '净利润TTM', '收盘价']]
    now_df['金融资产'] = 0
    now_df['公司债务'] = 0
    for factor1 in ['B_货币资金',
     'B_交易性金融资产',
     'B_衍生金融资产',
     'B_应收票据及应收账款',
     'B_应收票据',
     'B_应收账款',
     'B_应收款项融资',
     'B_应收利息',
     'B_应收股利',
     'B_其他应收款',
     'B_买入返售金融资产',
     'B_发放贷款及垫款',
     'B_可供出售金融资产',
     'B_持有至到期投资',
     'B_长期应收款',
     'B_投资性房地产',
     'B_长期股权投资']:
        now_df['金融资产'] += stock_df[factor1]
    for factor2 in ['B_短期借款',
     'B_交易性金融负债',
     'B_应付利息',
     'B_应付短期债券',
     'B_一年内到期的非流动负债',
     'B_长期借款',
     'B_应付债券',
     'B_租赁负债',
     'B_长期应付款(合计)']:
        now_df['公司债务'] += stock_df[factor2]   
    now_df['债务资本成本总额'] = stock_df['R_财务费用'] + stock_df['R_汇兑收益']
    now_df['经营资产自由现金流'] = stock_df['C_经营活动产生的现金流量净额'] - stock_df['C_固定资产折旧、油气资产折耗、生产性物资折旧'] - stock_df['C_无形资产摊销'] - stock_df['C_长期待摊费用摊销'] - stock_df['C_处置固定资产、无形资产和其他长期资产的损失']
    now_df['实际企业所得税税率'] = 1 - ((stock_df['R_四、利润总额'] - stock_df['R_减:所得税费用']) /  stock_df['R_四、利润总额'])
    now_df['少数股东权益比例'] = stock_df['B_少数股东权益'] / stock_df['B_所有者权益(或股东权益)合计']
    now_df['债务占比'] = now_df['公司债务'] / (stock_df['B_所有者权益(或股东权益)合计'] + now_df['公司债务'])
    now_df.drop_duplicates(subset=['财报日期'], inplace=True)
    now_df.reset_index(inplace=True)
    del now_df['index']
    print(now_df.tail(10))
    return now_df


def cal_WACC(now_df, time):
    WACC = (now_df['债务资本成本总额'] / ((now_df['公司债务'] + now_df['公司债务'].shift(time)) / 2) * now_df['债务占比'] * (1-now_df['实际企业所得税税率'])) + (0.09 * (1-now_df['债务占比']))
    return WACC.tolist()[-time]


def fcf_discounted(now_df, WACC, time, zero_change, one_change, two_change, three_change, g1, g2, g3, t1, t2):
    value = (now_df.loc[: ,'金融资产'].tolist()[-time] - now_df.loc[: ,'公司债务'].tolist()[-time])
    if zero_change == True:
        FCF = now_df.loc[: ,'经营资产自由现金流'].tolist()[-time] / WACC
    if one_change == True:
        FCF = (now_df.loc[: ,'经营资产自由现金流'].tolist()[-time] * (1+g1)) / (WACC - g1)
    if two_change == True: 
        temp_sum = 0
        for _ in t1:
            temp = now_df.loc[: ,'经营资产自由现金流'].tolist()[-time] * ((1+g1) ** _) / ((1+WACC) ** _)
            temp_sum = temp + temp_sum
        FCF = ((now_df.loc[: ,'经营资产自由现金流'].tolist()[-time] * ((1+g1) ** (t1[-1]-1)) * (1+g2)) / ((WACC-g2)*((1+WACC)**t1[-1]))) + temp_sum
    if three_change == True:
        temp_sum1, temp_sum2 = 0, 0
        for _ in t1:
            temp1 = now_df.loc[: ,'经营资产自由现金流'].tolist()[-time] * ((1+g1) ** _)
            temp =  temp1 / ((1+WACC) ** _)
            temp_sum1 = temp + temp_sum1
        for _ in t2:
            temp = temp1 * ((1+g2) ** _) / ((1+WACC) ** (_+t1[-1]))
            temp_sum2 = temp + temp_sum2
        FCF = (temp1 * ((1+g2) ** t2) * (1+g3)) / ((WACC-g3)*((1+WACC)**(t1[-1]+t2[-1]))) + temp_sum1 + temp_sum2
    FCF_plus_value =  (FCF + value)  * (1 - now_df.loc[: ,'少数股东权益比例'].tolist()[-time])
    result = FCF_plus_value / (now_df.loc[: ,'总市值'].tolist()[-time] / now_df.loc[: ,'收盘价'].tolist()[-time])  # 股票内在价值,价值/股数
    print('归属于上市公司股东的价值:', FCF_plus_value, '\n', '股票内在价值:', result)
    return FCF_plus_value, result


def statistics(now_df, time):
    PE1 = now_df.loc[: ,'总市值'].tolist()[-time] / now_df.loc[: ,'净利润TTM'].tolist()[-time]  # 市盈率
    PE2 = FCF_plus_value / now_df.loc[: ,'净利润TTM'].tolist()[-time] 
    print('原PE: ', PE1, '估值PE:', PE2)
    for time_n in [1, 2, 3, time, time+1, time+2, time+3]:
        print('前%s个季度股票价格' % (time_n-1), now_df.loc[: ,'收盘价'].tolist()[-time_n])  # 股票收盘价



#=== main
df, finance_df = read_file(file_name)
stock_df = merge_data(df, finance_df)
now_df = pd.DataFrame()
now_df = data_been_prepared(now_df, stock_df)
WACC = cal_WACC(now_df, time)
print('=============================')
print('WACC is ', WACC)
FCF_plus_value, result = fcf_discounted(now_df, WACC, time, zero_change, one_change, two_change, three_change, g1, g2, g3, t1, t2)
statistics(now_df, time)

Guess you like

Origin blog.csdn.net/food_for_thought/article/details/113181356