学习如何利用Python创建模型,以根据财务报表计算股票的公允价值。
有几种方法可以考虑,具体来说:
- 另一种方式是,公允价值是您可以支付并产生所需回报率的价格。
- 第三种方式是,公允价值是在当前倍数下包含财务未来增长的价格。当然,他们都假设您的分析和模型是正确的。随着央行利率政策正常化和投资回归基本面,这些方法越来越多地重新成为主流。
在本文中,将根据来自公司季度损益表和资产负债表的财务指标数据创建三种不同的模型。使用收益、自由现金流、收入、相应的增长和出色的股票作为模型的输入。这实际上进入了第三个假设,其中公允价值是在给定当前倍数的情况下包含未来增长的价格。
对于高质量的基本面数据,使用EOD的 API,对于价格数据,使用 yfinance。
项目主要特点
下载给定公司的基本面数据(资产负债表、损益表、现金流量表)。
可视化股票的价格序列并将市场价格与公允价格进行比较。在开始之前,需要以下内容:
Jupyter Notebook:为操作系统安装 Anaconda的个人版本,已经安装了 Jupyter。
-
EOD API 密钥:按照此处的说明进行操作。一旦设置好了,就可以继续执行了。
让我们开始吧
加载整个项目所需的包并启动需要的变量。
包是一组结构化函数,可以导入到 Python 解释器中,然后可以从中调用和使用。
import matplotlib.pyplot as plt
import requests
import pandas as pd
from datetime import *
import numpy as np
import json
import urllib.request
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')
EOD_API_KEY = 'your-api-key'
SYMBOL = 'FVRR.US'
为这个演示选择了 Fiverr 股票 (FVRR),因为喜欢这家公司,而且评估其当前的公平价格和潜在的未来增长有点困难,所以这仍然是一个有趣的练习。但是,最终会将整个代码组合成一个函数,该函数可以对指定给任何股票进行此评估!
现在,在导入了所有需要的包之后,下面从使用的函数开始。
1. 下载给定公司的基本面数据
def get_data(symbol):
url = f'https://eodhistoricaldata.com/api/fundamentals/{symbol}?api_token={EOD_API_KEY}'
response = urllib.request.urlopen(url)
data = json.loads(response.read())
return data
data = get_data(symbol=SYMBOL)
print(data.keys())
对于给定的股票,可以获得相当多的数据,但是对于公式,将使用“财务”、“收益”和“杰出股票”。
2. 基于自由现金流的估值
𝐹𝑉(𝑓𝑐𝑓)=𝑓𝑐𝑓p𝑠∗(1+𝑓𝑐𝑓𝑔𝑟𝑜𝑤𝑡ℎ)∗(𝑝𝑟𝑖𝑐𝑒/𝑓𝑐𝑓p𝑠)
`fcf = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()['freeCashFlow'].astype(float)[-1]
fcf_growth = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()\
['freeCashFlow'].astype(float).pct_change().rolling(20).mean()[-1]
shares_outs = round(pd.DataFrame(data['outstandingShares']['quarterly']).T.set_index('dateFormatted')\
.sort_index()['shares'].astype(float)[-1],0)
price = yf.download(SYMBOL.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]
fcf_per_share = fcf/shares_outs
print(f'fcf_fair_value: {fcf_per_share*(1+fcf_growth)*(price/fcf_per_share)}')``
对于基于自由现金流的估值,使用最新的季度自由现金流fcf
,然后使用过去20个季度的平均季度自由现金流变化作为预期指标fcf_growth
,然后使用流通股shares_outs
和最新价格。至于price
计算每股自由现金流量和最终公式。
3. 基于收入/销售的估值
𝐹𝑉(𝑠𝑎𝑙𝑒𝑠)=𝑠𝑝𝑠∗(1+𝑠𝑎𝑙𝑒𝑠𝑔𝑟𝑜𝑤𝑡ℎ)∗(𝑝𝑟𝑖𝑐𝑒/𝑠𝑝𝑠)
sales = pd.DataFrame(data['Financials']['Income_Statement'
]['quarterly']).T.sort_index()\
['totalRevenue'].astype(float)[-1]
sales_growth = pd.DataFrame(data['Financials']['Income_Statement']['quarterly']).T.sort_index()\
['totalRevenue'].astype(float).pct_change().rolling(20).mean()[-1]
price = yf.download(SYMBOL.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]
shares_outs = round(pd.DataFrame(data['outstandingShares']['quarterly']).T.set_index('dateFormatted')\
.sort_index()['shares'].astype(float)[-1],0)
sales_per_share = sales/shares_outs
print(f'sps_fair_value: {sales_per_share*(1+sales_growth)*(price/sales_per_share)}')
对于基于收入/销售的估值,使用最新的季度总收入作为sales
,然后sales_growth
使用过去 20 个季度的平均总收入变化,然后再次使用流通股作为shares_outs
和最新价格price
来计算每分享和最终公式。
4. 基于收益的估值
𝐹𝑉(𝑝𝑒)=𝑒𝑝𝑠∗(1+𝑒𝑝𝑠𝑔𝑟𝑜𝑤𝑡ℎ)∗𝑝𝑒
eps = pd.DataFrame(data['Earnings']['Annual']).T.sort_index()['epsActual'][-1]
eps_growth = np.mean(pd.DataFrame(data['Earnings']['Trend']).T.sort_index()\
[
'earningsEstimateGrowth'].astype(float).values)
price = yf.download(SYMBOL.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]
pe = price/eps
print(f'pe_fair_value: {eps*(1+eps_growth)*pe}')
对于基于收益的估值,用年收益作为 eps,接下来使用可用的 EarningsEstimatedGrowth 作为 eps_growth,使用最新的股价来计算最新的市盈率和公式。
在完成所有三个方法之后,现在可以将所有内容放在一个函数中,该函数将交易品种名称作为参数并计算所有三个公允价值并与当前股价进行比较!
5. 把它们放在一起
import matplotlib.pyplot as plt
import requests
import pandas as pd
from datetime import *
import numpy as np
import json
import urllib.request
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')
EOD_API_KEY = 'your_api_key'
SYMBOL = 'FVRR.US'
def get_data(symbol, api_key):
url = f'https://eodhistoricaldata.com/api/fundamentals/{symbol}?api_token={EOD_API_KEY}'
response = urllib.request.urlopen(url)
data = json.loads(response.read())
return data
def get_financial_metrics_valuation(data):
eps = pd.DataFrame(data['Earnings']['Annual']).T.sort_index()['epsActual'][-1]
eps_growth = np.mean(pd.DataFrame(data['Earnings']['Trend']).T.sort_index()['earningsEstimateGrowth'].astype(float).values)
fcf = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()['freeCashFlow'].astype(float)[-1]
fcf_growth = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()\
['freeCashFlow'].astype(float).pct_change().rolling(20).mean()[-1]
sales = pd.DataFrame(data['Financials']['Income_Statement']['quarterly']).T.sort_index()['totalRevenue'].astype(float)[-1]
sales_growth = pd.DataFrame(data['Financials']['Income_Statement']['quarterly']).T.sort_index()\
['totalRevenue'].astype(float).pct_change().rolling(20).mean()[-1]
shares_outs = round(pd.DataFrame(data['outstandingShares']['quarterly']).T.set_index('dateFormatted')\
.sort_index()['shares'].astype(float)[-1],0)
price = yf.download(SYMBOL.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]
pe = price/eps
fcf_per_share = fcf/shares_outs
sales_per_share = sales/shares_outs
df = {}
df['pe_intrinsic_value'] = eps*(1+eps_growth)*pe
df['fcf_intrinsic_value'] = fcf_per_share*(1+fcf_growth)*(price/fcf_per_share)
df['sales_intrinsic_value'] = sales_per_share*(1+sales_growth)*(price/sales_per_share)
df['current_price'] = price
return pd.DataFrame(df, index=pd.Series(SYMBOL))
data = get_data(symbol=SYMBOL, api_key=EOD_API_KEY)
fair_values = get_financial_metrics_valuation(data)
fair_values
最终得到了一个易于使用的小脚本,可以使用来自 EOD 的 API 的数据从根本上评估想要的任何股票。从目前的分析中,可以看到 FVRR 的当前价格可能被略微低估了。此外,可以观察到三个模型价格对应的股票价格和绘图线,这样更容易直观地了解股票当前的位置。
最后的想法
尽管估值模型被提示假设和错误,但通过一些简单的数学和质量数据,能够估计给定股票的一系列公允价值。这可以很容易地增强投资分析能力,并提供精确的基线或起点!
但是,建议在遇到投资分析、交易技术或策略时始终遵循以下步骤:
- 确保已经使用现实生活中的模拟和条件对其进行了回测。
加油吧!
手把手将Visual Studio Code变成Python开发神器
Python 处理 PDF 的神器 -- PyMuPDF