如何应用Python助你在股票中获利?

学习如何利用Python创建模型,以根据财务报表计算股票的公允价值。

扫码关注《Python学研大本营》,加入读者群,分享更多精彩

股票市场中需要低买高卖的投资策略,必须以合理的价格购买投资,这意味着以公允价值购买。但是如何计算股票的公允价值或内在价值呢?

有几种方法可以考虑,具体来说:

  • 一种方法是公允价值是未来自由现金流的现值。

  • 另一种方式是,公允价值是可以支付并产生所需回报率的价格。

  • 第三种方式是,公允价值是在当前倍数下包含财务未来增长的价格。

当然,他们都假设分析和模型是正确的。随着央行利率政策正常化和投资回归基本面,这些方法越来越多地重新成为主流。

在本文中,我们将根据来自公司季度损益表和资产负债表的财务指标数据创建三种不同的模型。使用收益、自由现金流、收入、相应的增长和出色的股票作为模型的输入。这实际上进入了第三个假设,其中公允价值是在给定当前倍数的情况下包含未来增长的价格。

对于高质量的基本面数据,使用EOD的 API,对于价格数据,使用 yfinance。

项目特点

  • 下载给定公司的基本面数据(资产负债表、损益表、现金流量表)。

  • 计算财务指标模型的输入。

  • 将输入插入每个模型并估计公司的公平价格。

  • 可视化股票的价格序列并将市场价格与公允价格进行比较。

需要准备以下内容:

  • Python:安装 3.9.7 版本。

  • 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 的当前价格可能被略微低估了。此外,可以观察到三个模型价格对应的股票价格和绘图线,这样更容易直观地了解股票当前的位置。

最后的想法

尽管估值模型被提示假设和错误,但通过一些简单的数学和质量数据,能够估计给定股票的一系列公允价值。这可以很容易地增强我们的投资分析能力,并为我们提供精确的基线或起点!

但是,建议您在遇到投资分析、交易技术或策略时始终遵循以下步骤:

  • 对所做的每一个分析都有批判的心态。

  • 确保已经使用现实生活中的模拟和条件对其进行了回测。

  • 如果有好的潜力,请通过前瞻测试。

加油吧!

推荐书单

《Python数据可视化》

购买链接: https://item.jd.com/12670073.html

《Python数据可视化》详细阐述了与Python数据可视化相关的基本解决方案,主要包括数据可视化和数据探索的重要性、绘图知识、Matplotlib、利用Seaborn简化可视化操作、绘制地理空间数据、基于Bokeh的交互式操作等内容。此外,该书还提供了相应的示例、代码,以帮助读者进一步理解相关方案的实现过程。

《Python数据可视化》适合作为高等院校计算机及相关专业的教材和教学参考书,也可作为相关开发人员的自学教材和参考手册。

精彩回顾

可视化案例研究——以智利总统选举为例

想用Python赚钱?——安排!

【案例】如何使用Flask构建天气预报 

扫码关注《Python学研大本营》,加入读者群,分享更多精彩