之前写公司研究报告时,所有的数据都是通过翻看招股说明书/年报的PDF获取的,把数字从PDF里复制粘贴到EXCEL里再生成图表的过程非常繁琐,而且容易因为看错行/列摘错数据。使用Python可以实现提取财务数据、计算财务指标、可视化呈现等一系列操作的自动化,加之可以通过爬虫从巨潮资讯、雪球等网站批量获取公司财务报表,在没有专用数据库可用的情况下,Python能为财务大数据分析、大范围的公司研究、行业研究提供极大便利。
一、获取报表
巨潮资讯上的数据最权威,但是各个年度数据是分开存放的,而雪球提供财务数据是各年度都在同一个CSV里。由于经营分析涉及趋势研究,需要连续多年的数据,在数据按年度分为多个文件的情况下还得多进行一次遍历,不是很方便,故这里选择从雪球批量获取财务报表。
import pandas as pd
import requests
from multiprocessing.dummy import Pool as ThreadPool
lrb_base_url = 'http://api.xueqiu.com/stock/f10/incstatement.csv?page=1&size=10000&symbol='
llb_base_url = 'http://api.xueqiu.com/stock/f10/cfstatement.csv?page=1&size=10000&symbol='
fzb_base_url = 'http://api.xueqiu.com/stock/f10/balsheet.csv?page=1&size=10000&symbol='
header = {'User-Agent': 'Mozilla/5.0'}
定义利润表、资产负债表、现金流量表的下载函数:
def download_lrb(url):
r = requests.get(url, headers=headers)
filename = url.split('=')[-1] + '_lrb.csv'
print(filename)
with open(filename, 'wb') as f:
f.write(r.content)
def download_fzb(url):
r = requests.get(url, headers=headers)
filename = url.split('=')[-1] + '_fzb.csv'
print(filename)
with open(filename, 'wb') as f:
f.write(r.content)
def download_llb(url):
r = requests.get(url, headers=headers)
filename = url.split('=')[-1] + '_llb.csv'
print(filename)
with open(filename, 'wb') as f:
f.write(r.content)
股票代码.txt存放需要分析的公司的股票代码,格式为SHXXXXXX或SZXXXXXX
with open('股票代码.txt', 'r', encoding='utf-8') as f:
symbol = [s.strip() for s in f.readlines()]
lrb_urls = [lrb_base_url + i for i in symbol]
fzb_urls = [fzb_base_url + i for i in symbol]
llb_urls = [llb_base_url + i for i in symbol]
pool = ThreadPool(10)
pool.map(download_lrb, lrb_urls)
pool.close()
pool.join()
pool = ThreadPool(10)
pool.map(download_fzb, fzb_urls)
pool.close()
pool.join()
pool = ThreadPool(10)
pool.map(download_llb, llb_urls)
pool.close()
pool.join()
美的集团SZ000333
格力电器SZ000651
下载结果:
二、以格力电器为例的经营分析
经营分析主要包括两个方面:公司的业绩和公司的经营水平。相关数据可从利润表和资产负债表获取。
# 导入所需要的库
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
输入要分析的公司的股票代码
# 股票代码
stock_0 = 'SZ000651' #格力电器
# 输入年份(输入年报数据截止年份的下一年)
year = 2018
定义函数:读取csv文件,转化为DataFrame格式
def get_lrb(stock_id):
# 读取利润表数据
lrb = pd.read_csv(
r'data/%s_lrb.csv' % (stock_id),
encoding='utf-8',
header=0,
index_col=None)
list_lrb = [] #获取所有截止日期的列表
for i in lrb['报表期截止日']:
list_lrb.append(str(i))
list_lrb_0 = [] #将20171231的格式转换为2017-12-31
for i in list_lrb:
i_ = i[:4] + '-' + i[4:6] + '-' + i[6:8]
list_lrb_0.append(i_)
lrb['报告时间'] = [pd.to_datetime(t) for t in list_lrb_0] #转为日期数据类型
lrb.index = lrb['报告时间'] #重新设置利润表索引
data_lrb = lrb[::-1] #由于csv中为时间倒序,将表倒序排列使之呈时间正序
return data_lrb
def get_fzb(stock_id):
# 读取资产负债表表数据
fzb = pd.read_csv(
r'data/%s_fzb.csv' % (stock_id),
encoding='utf-8',
header=0,
index_col=None)
list_fzb = []
for i in fzb['报表日期']:
list_fzb.append(str(i))
list_fzb_0 = []
for i in list_fzb:
i_ = i[:4] + '-' + i[4:6] + '-' + i[6:8]
list_fzb_0.append(i_)
fzb['报告时间'] = [pd.to_datetime(t) for t in list_fzb_0]
fzb.index = fzb['报告时间']
data_fzb = fzb[::-1]
return data_fzb
定义函数:筛选出年报数据,根据月份筛选,12月的即为年报数据
def get_data_month(data, month):
data_month = data[data.index.month == month]
return data_month
定义函数:实现数据可视化
def data_plot(data, y, legend='格力电器', kind='bar'):
l_0 = len(data)
s_0 = list(range(l_0))
x_0 = np.array(s_0)
y_0 = tuple([str(i) for i in range(year - l_0, year)])
data[y].plot(kind=kind)
plt.title(y)
plt.legend([legend], loc='upper left')
plt.xticks(x_0, y_0)
plt.grid(color='#95a5a6', linestyle='--', linewidth=1, axis='y',alpha=0.4)
plt.savefig(r'pic/%s.png' % (y,))
return
(1)公司业绩分析
公司业绩主要看业绩结果和业绩质量。业绩结果的主要指标为营业收入、毛利、净利;业绩质量的主要指标为毛利率、净利率、成本费用率。数据全部来自利润表。
定义函数:计算业绩分析需要的指标
# 经营分析:公司业绩
def get_performance(data):
result_p = pd.DataFrame()
#业绩结果
result_p['营业收入'] = data['营业收入']
result_p['毛利'] = data['营业收入'] - data['营业成本']
result_p['净利'] = data['归属于母公司所有者的净利润']
#业绩质量
result_p['毛利率'] = (data['营业收入'] - data['营业成本']) / data['营业收入']
result_p['净利率'] = data['归属于母公司所有者的净利润'] / data['营业收入']
result_p['成本费用率'] = (data['营业成本']+data['营业税金及附加']+data['销售费用']+data['管理费用']+data['财务费用'] )/ data['营业收入']
result_p.index = data['报告时间']
return result_p
实现数据读取、筛选、计算、可视化
# 读取数据
data_lrb = get_lrb(stock_0) #数据全部来自利润表
# 筛选年报数据
data_lrb_year = get_data_month(data_lrb, 12)
# 计算所需指标
result_p = get_performance(data_lrb_year)
#可视化
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] #显示中文
for i in ['营业收入','毛利','净利','毛利率','净利率','成本费用率']:
data_plot(result_p, i)
plt.show()
近年毛利率基本保持稳定,净利率稳步高速增长,成本费用率有降低的趋势,说明经营效率在逐步提高。
(2)公司经营水平分析
经营水平的核心指标有净利率、资产周转率、资产负债率、净资产收益率。数据来自利润表和资产负债表。
定义函数:计算经营水平分析需要的指标
# 经营分析:经营水平
def get_management(data):
result_m = pd.DataFrame()
result_m['净利率'] = data['归属于母公司所有者的净利润'] / data['营业收入']
result_m['资产周转率'] = data['营业收入'] / data['资产总计']
result_m['资产负债率'] = data['负债合计'] / data['资产总计']
result_m['净资产收益率'] = data['归属于母公司所有者的净利润'] / (data['资产总计']-data['负债合计'])
result_m.index = data.index
return result_m
实现数据读取、筛选、计算、可视化:
#读取数据,数据来自利润表和资产负债表
data_lrb = get_lrb(stock_0)
data_fzb = get_fzb(stock_0)
#筛选年报数据
data_lrb_year = get_data_month(data_lrb, 12)
data_fzb_year = get_data_month(data_fzb, 12)
#将利润表和资产负债表合为一表,方便计算
data=pd.merge(left=data_lrb_year.drop(columns = ['报告时间']),right=data_fzb_year.drop(columns = ['报告时间']),on="报告时间")
#计算所需指标
result_m = get_management(data)
#可视化
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
for i in ['净利率','资产周转率','资产负债率','净资产收益率']:
data_plot(result_m, i)
plt.show()
这一类指标通常需要与竞争对手相比较才能判断优劣,后续深入分析可以将同类公司(如美的、奥克斯…)的股票代码做成一个列表,使用for循环批量下载财务报表、将相关指标可视化进行对比分析。
三、风险分析
这里深入分析一下格力的资产负债率,即使不进行同类对比,也可以看出格力的资产负债率非常高,几乎始终在70%~80%之间,资产负债率普遍较高的房地产企业平均水平也就70%左右(此处只是举例,资产负债率不能跨行业对比)。理论上高资产负债率的企业风险更大(偿债压力大),那么格力这么高的资产负债率是否真的意味着高风险?
理论上讲,高资产负债率带来的风险即偿债风险,公司面临较大偿债压力,因此需要进一步探究企业的流动负债结构以判别高风险是否真实存在。
定义函数:分析流动负债结构
def get_structure(data):
result_s = pd.DataFrame()
data=data.fillna(0) #填充空值为0
result_s['经营性负债占比'] = (data['应付账款']+data['应付票据']+data['预收款项']) / data['流动负债合计']
result_s['贷款占比'] = (data['短期借款']+data['一年内到期的非流动负债']) / data['流动负债合计']
result_s.index = data['报告时间']
return result_s
实现数据读取、筛选、计算、可视化:
data_fzb = get_fzb(stock_0)
data_fzb_year = get_data_month(data_fzb, 12)
result_s = get_structure(data_fzb_year)
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
for i in ['经营性负债占比','贷款占比']:
data_plot(result_s, i)
plt.show()
可以看出格力的经营性负债一直较高,在1999-2006甚至达到了80%或90%以上,即便近年有所下降依然处于40%的高位,而贷款占比很少,近年有所上升也不到15%,由此可以看出,高资产负债率的格力实际上偿债风险并不高。流动负债中会带来偿债压力的贷款占比很少,经营性负债才是主体,而大量的应付款项和预收款项实际上是供应商、经销商资金的占用,在产品不愁销路的情况下一般不会有什么问题,反而是竞争力的体现,这说明格力在整个产业链中非常强势。
本文只是展示使用Python进行财务分析/公司研究的大概思路和流程,分析本身存在不严谨之处。定义好计算相关指标的函数后,只要列出研究对象的股票代码,使用for循环便能实现快速披露获取报表、读取数据、处理数据、计算指标并最终实现可视化,使分析效率大大提高。