说明:本节使用的财报数据下载方式参见 https://www.jianshu.com/p/fdb673ed8c9b
在财报分析系列文章中,小鱼分别介绍了如何读取和分析资产负债表、利润表以及现金流量表,并且将分析成果保存成 Word 文档输出。
不过,之前的系列文章中代码的部分小鱼未做太多整合,所以整体篇幅比较长,这样做的目的呢,也是为了通过大量输入的代码,来熟悉 Pandas 的 API。接下来的系列文章中,小鱼将对代码部分进行整合,向上抽取出公共的函数,提高代码的可扩展性。
定义配置文件 ini.json
在之前进行分析时,无论是分析的主题,图片保存的名称,还是列名都是直接定义为变量,或者使用字面量。这样的方式很不友好,当我们想要分析其他公司的时候,就必须要复制到新的 Jupyter NoteBook 中。
为此,小鱼定义了一个 ini.json 的配置文件,感兴趣的读者朋友只需要保存该配置文件,打开新的 Jupyter NoteBook 笔记时只需要从配置文件中加载变量即可。
{
"debt": {
"auto": [
"资产合计(元)",
"负债合计(元)",
"货币资金(元)",
"交易性金融资产(元)",
"一年内到期的非流动负债(元)",
"短期借款(元)",
"一年内到期的非流动负债(元)",
"长期借款(元)",
"应付债券(元)",
"其中:应付票据(元)",
"应付账款(元)",
"预收款项(元)",
"合同负债(元)",
"其中:应收票据(元)",
"合同资产(元)",
"应收账款(元)",
"预付款项(元)",
"固定资产合计(元)",
"在建工程合计(元)",
"以公允价值计量且其变动计入当期损益的金融资产(元)",
"债权投资(元)",
"可供出售金融资产(元)",
"其他权益工具投资(元)",
"持有至到期投资(元)",
"其他非流动金融资产(元)",
"长期股权投资(元)",
"投资性房地产(元)",
"资产合计(元)",
"存货(元)",
"商誉(元)",
"归属于母公司所有者权益合计(元)"
],
"hands": [
"其他流动资产里的理财产品",
"其他流动资产里的结构性存款",
"长期应付款",
"应收款项融资",
"工程物资"
]
},
"benefit": {
"auto": [
"其中:营业收入(元)",
"其中:营业成本(元)",
"销售费用(元)",
"管理费用(元)",
"研发费用(元)",
"财务费用(元)",
"营业税金及附加(元)",
"三、营业利润(元)",
"五、净利润(元)",
"归属于母公司所有者的净利润(元)"
]
},
"cash": {
"auto": [
"经营活动产生的现金流量净额(元)",
"购建固定资产、无形资产和其他长期资产支付的现金(元)",
"经营活动产生的现金流量净额(元)",
"投资活动产生的现金流量净额(元)",
"筹资活动产生的现金流量净额(元)"
]
},
"images": [
["总资产","总资产增长率"],
"资产负债率",
["准货币资金与有息负债", "准货币资金与有息负债的差额"],
["应付预收与应收预付","应收预付明细","应付预收减应收预付的差额"],
"应收账款和合同资产占总资产的比率",
"固定资产工程占总资产的比率",
"投资类资产占总资产的比率",
"存货和应收账款情况",
"商誉占总资产的比率",
["营业收入","营业收入增长率"],
["毛利率","毛利率波动幅度"],
["期间费用率","期间费用率占毛利率的比例"],
"销售费用率",
["主营利润率","主营利润占营业利润的比率"],
"",
["净资产收益率ROE","归属于母公司所有者的净利润增长率"],
"购建支付的现金与经营活动产生的现金流量净额的比率",
"分配股利、利润或偿付利息支付的现金与经营活动产生的现金流量净额的比率",
"三大活动的现金流量净额情况"
],
"titles": [
"一.看总资产,判断公司实力及扩张能力",
"二.看资产负债率,了解公司的偿债风险",
"三.看有息负债和准货币资金,排除偿债风险",
"四.看“应付预收”减“应收预付”的差额,了解公司的竞争优势",
"五.看应收账款、合同资产,了解公司的产品竞争力",
"六.看固定资产,了解公司维持竞争力的成本",
"七.看投资类资产,判断公司的专注程度",
"八.看存货,了解公司未来业绩爆雷的风险",
"九.看商誉,了解公司未来爆雷的风险",
"十.看营业收入,了解公司的行业地位及成长性",
"十一.看毛利率,了解公司的产品竞争力及风险",
"十二.看期间费用率,了解公司的成本管控能力",
"十三.看销售费用率,了解公司产品的销售难易度",
"十四.看主营利润,了解公司主业的盈利能力及利润质量",
"十五.看净利润,了解公司的经营成果及含金量,净利润主要看净利润含金量",
"十六.看归母净利润,了解公司的整体盈利能力及持续性",
"十七.看购买固定资产、无形资产和其他长期资产支付的现金,了解公司的增长潜力",
"十八.看分配股利、利润或偿付利息支付的现金,了解公司的现金分红情况",
"附:三大活动产生的现金流量净额"
],
"tables_fields": {
"t1": {
"columns": ["资产合计(元)", "总资产增长率"],
"auto": ["资产合计(元)"]
},
"t2": {
"columns": ["负债合计(元)", "资产合计(元)", "资产负债率"],
"auto": ["负债合计(元)", "资产合计(元)"]
},
"t3": {
"columns": ["货币资金(元)", "交易性金融资产(元)", "其他流动资产里的理财产品", "其他流动资产里的结构性存款", "准货币资金",
"短期借款(元)", "一年内到期的非流动负债(元)", "长期借款(元)", "应付债券(元)", "长期应付款", "有息负债总额",
"总货币资金与有息负债之差"],
"auto": ["货币资金(元)", "交易性金融资产(元)", "其他流动资产里的理财产品", "其他流动资产里的结构性存款", "短期借款(元)",
"一年内到期的非流动负债(元)", "长期借款(元)", "应付债券(元)", "长期应付款"]
},
"t4": {
"columns": ["其中:应付票据(元)", "应付账款(元)", "预收款项(元)", "合同负债(元)", "应付与预收合计", "其中:应收票据(元)",
"合同资产(元)", "应收款项融资", "应收账款(元)", "预付款项(元)", "应收与预付合计", "应付预收减应收预付的差额"],
"auto": ["其中:应付票据(元)", "应付账款(元)", "预收款项(元)", "合同负债(元)", "其中:应收票据(元)", "合同资产(元)",
"应收款项融资","应收账款(元)", "预付款项(元)"]
},
"t5": {
"columns": ["合同资产(元)", "应收账款(元)", "应收账款+合同资产", "资产合计(元)", "(应收账款+合同资产)占总资产的比率"],
"auto": ["合同资产(元)", "应收账款(元)", "资产合计(元)"]
},
"t6": {
"columns": ["固定资产合计(元)", "在建工程合计(元)", "工程物资", "固定资产+在建工程+工程物资", "资产合计(元)",
"固定资产工程占总资产的比率"],
"auto": ["固定资产合计(元)", "在建工程合计(元)", "工程物资", "资产合计(元)"]
},
"t7": {
"columns": ["以公允价值计量且其变动计入当期损益的金融资产(元)", "债权投资(元)", "可供出售金融资产(元)",
"其他权益工具投资(元)", "持有至到期投资(元)", "其他非流动金融资产(元)", "长期股权投资(元)","投资性房地产(元)",
"投资类资产合计", "资产合计(元)", "投资类资产占总资产的比率"],
"auto": ["以公允价值计量且其变动计入当期损益的金融资产(元)", "债权投资(元)", "可供出售金融资产(元)",
"其他权益工具投资(元)", "持有至到期投资(元)", "其他非流动金融资产(元)", "长期股权投资(元)","投资性房地产(元)",
"资产合计(元)"]
},
"t8": {
"columns": ["存货(元)", "资产合计(元)", "存货占总资产的比率"],
"auto": ["存货(元)", "资产合计(元)"]
},
"t9": {
"columns": ["商誉(元)", "资产合计(元)", "商誉占总资产的比率"],
"auto": ["商誉(元)", "资产合计(元)"]
},
"t10": {
"columns": ["其中:营业收入(元)", "营业收入增长率"],
"auto": ["其中:营业收入(元)"]
},
"t11": {
"columns": ["其中:营业收入(元)", "其中:营业成本(元)", "毛利率", "毛利率波动率"],
"auto": ["其中:营业收入(元)", "其中:营业成本(元)"]
},
"t12": {
"columns": ["销售费用(元)","管理费用(元)", "研发费用(元)", "财务费用(元)", "四费合计", "其中:营业收入(元)",
"期间费用率", "毛利率", "期间费用率占毛利率的比率"],
"auto": ["销售费用(元)","管理费用(元)", "研发费用(元)", "财务费用(元)", "其中:营业收入(元)"]
},
"t13": {
"columns": ["销售费用(元)","其中:营业收入(元)", "销售费用率"],
"auto": ["销售费用(元)","其中:营业收入(元)"]
},
"t14": {
"columns": ["其中:营业收入(元)","其中:营业成本(元)", "营业税金及附加(元)", "四费合计", "主营利润", "主营利润率",
"三、营业利润(元)", "主营利润占营业利润的比率"],
"auto": ["其中:营业收入(元)","其中:营业成本(元)", "营业税金及附加(元)", "三、营业利润(元)"]
},
"t15": {
"columns": ["经营活动产生的现金流量净额(元)", "五、净利润(元)", "净利润现金比率"],
"auto": ["经营活动产生的现金流量净额(元)", "五、净利润(元)"]
},
"t16": {
"columns": ["归属于母公司所有者的净利润(元)", "归属于母公司所有者权益合计(元)", "ROE 净资产收益率",
"归属于母公司所有者的净利润增长率"],
"auto": ["归属于母公司所有者的净利润(元)", "归属于母公司所有者权益合计(元)"]
},
"t17": {
"columns": ["购建固定资产、无形资产和其他长期资产支付的现金(元)", "经营活动产生的现金流量净额(元)",
"购建支付的现金与经营活动产生的现金流量净额的比率"],
"auto": ["购建固定资产、无形资产和其他长期资产支付的现金(元)", "经营活动产生的现金流量净额(元)"]
},
"t18": {
"columns": ["分配股利、利润或偿付利息支付的现金(元)","经营活动产生的现金流量净额(元)",
"分配股利、利润或偿付利息支付的现金占经营活动产生的现金流量净额的比率"],
"auto": ["分配股利、利润或偿付利息支付的现金(元)","经营活动产生的现金流量净额(元)"]
},
"t19": {
"columns": ["经营活动产生的现金流量净额(元)", "投资活动产生的现金流量净额(元)", "筹资活动产生的现金流量净额(元)",
"三大活动现金流量净额类型"],
"auto": ["经营活动产生的现金流量净额(元)", "投资活动产生的现金流量净额(元)", "筹资活动产生的现金流量净额(元)"]
}
}
}
下面,我们读配置文件中的信息做一个简单介绍。
json 最外层的 debt
benefit
cash
分别表示资产负债表字段、利润表字段以及现金流量表字段。
其中,benefit
和 cash
不包含需要手动赋值的字段 hands
,这是因为我们下载的利润表和现金流量表可以成功导出所有我们分析需要的字段。
资产负债表 debt
中包含了 auto
和 hands
两部分,hands
部分定义的项目在读取时需要手动查找年报数据,进行录入。
说明:其他流动资产里的理财产品和结构性存款如果有,会在财务报表附注中说明;长期应付款我们在分析准货币资金和有息负债时须要用到,要通过年报查找长期应付款是否为有息;应收款项融资有的公司导不出来;2018年之后工程物资并入在建工程,在分析2017年及之前的财务数据时,需要手动查找工程物资。
接下来的字段是 images
和 titles
,保存的是图片的名称以及标题信息。最后是 tables_fields
,保存了每个分析子表的信息,其中 columns
为子表的列名称,在初始化 DataFrame 时使用,auto
中的列可以直接从总表中读取并复制,未出现在 auto
中的列则需要计算得出。
读取配置文件 ini.json
读取 json 格式的配置文件:
import json
with open('./ini.json', encoding='utf-8') as f:
config = json.load(f)
images = config['images']
titles = config['titles']
tables_fields = config['tables_fields']
读取和处理财务数据
导入和配置 Pandas ,将三大财务报表的文件名定义为常量:
import pandas as pd
pd.set_option('display.float_format', lambda x: '%.2f' % x)
pd.set_option('mode.chained_assignment', None)
DEBT = 'xxxxxx_debt_year.xls'
BENEFIT = 'xxxxxx_benefit_year.xls'
CASH = 'xxxxxx_cash_year.xls'
读取同花顺个股下载的资产负债表:
debt_df = pd.read_excel(DEBT, sheet_name='Worksheet', header=1, index_col=0)
# 删除空行
debt_df.dropna(inplace=True)
# 将省略符号 '--' 替换为 0
debt_df.replace({'--': 0}, inplace=True)
# 取最近 6 年的数据,正序排列
debt_df = debt_df.T[:6][::-1]
debt_df.columns.name = ''
读取的资产负债表(长度关系只展示部分列):
同花顺个股在导出资产负债表、利润表、现金流量表时,如果某个科目历年的数据全部为空,该字段将不会被导出。
下面,我们遍历 auto
中的字段,如果该字段在 debt_df
的列字段中不存在,则赋值为 0 。
for field in config['debt']['auto']:
if field not in debt_df:
print(field)
debt_df[field] = [0,0,0,0,0,0]
执行过程中,会将不存在的列打印出来,并为该列赋值为 0 :
长期借款(元)
应付债券(元)
合同资产(元)
以公允价值计量且其变动计入当期损益的金融资产(元)
债权投资(元)
持有至到期投资(元)
其他非流动金融资产(元)
接下来是资产负债表需要手动查找的科目:
>> config['debt']['hands']
['其他流动资产里的理财产品', '其他流动资产里的结构性存款', '长期应付款', '应收款项融资', '工程物资']
查找历年年报,小鱼得到如下结果:
# 从15年开始依次录入
debt_df['其他流动资产里的理财产品'] = [0,0,1500000000,2570000000,0,0]
debt_df['其他流动资产里的结构性存款'] = [0,0,0,0,0,0]
debt_df['长期应付款'] = [0,0,0,0,0,0]
debt_df['应收款项融资'] = [0,0,0,0,408972104.07,0]
debt_df['工程物资'] = [0,0,0,0,0,0]
至此资产负债表的数据就处理完啦~
debt_df.apply(format_thousandth).T
为了方便查看,下面输出的是 benefit_df
的转置。
同样的方式,完成利润表的处理:
benefit_df = pd.read_excel(BENEFIT, sheet_name='Worksheet', header=1, index_col=0)
benefit_df.dropna(inplace=True)
benefit_df.replace({'--':0}, inplace=True)
benefit_df = benefit_df.T[:6][::-1]
benefit_df.columns.name = ''
for field in config['benefit']['auto']:
if field not in benefit_df:
print(field)
benefit_df[field] = [0,0,0,0,0,0]
在执行以上代码时,一般情况下不会打印出任何缺失的科目,我们需要分析的科目都是包含的。
利润表的转置:
benefit_df.apply(format_thousandth).T
利润表部分科目:
最后是现金流量表的处理:
cash_df = pd.read_excel(CASH, sheet_name='Worksheet', header=1, index_col=0)
cash_df.dropna(inplace=True)
cash_df.replace({'--':0}, inplace=True)
cash_df = cash_df.T[:6][::-1]
cash_df.columns.name = ''
for field in config['cash']['auto']:
if field not in cash_df:
print(field)
cash_df[field] = [0,0,0,0,0,0]
现金流量表部分科目:
合并三张报表
最后,我们将三份报表的数据合并到一起:
>> df = pd.concat([debt_df, benefit_df, cash_df], axis=1)
>> df
合并后的结果如下:
合并后的报表一共包含 161 个会计科目。将结果保存到 csv 文件,这样我们需要分析的时候,直接读取处理好的 CSV 就好了。
df.to_csv('data.csv')
以上就是今天的全部内容啦连载的下一篇文章,我们将继续优化财务报表分析步骤中的代码。下节见啦