SPL 量化系列实践:计算行业财务指标
行业财务指标非常多,本文只是抛砖引玉,以四个常用的行业财务指标为例介绍SPL计算财务指标的方法,其他财务指标可以用类似的方法计算出来。
1. 行业毛利润率
行业毛利润率=(营业收入-营业成本)/营业收入
其中:
营业收入=行业所有上市公司营业收入之和
营业成本=行业所有上市公司营业成本之和
上市公司的营业收入、营业成本都可以在利润表中查到。
2. 行业净利润率
行业净利润率=净利润/营业总收入
其中:
净利润=行业所有上市公司净利润之和
营业总收入=行业所有上市公司营业总收入之和
上市公司的净利润、营业总收入都可以在利润表中查到。
3. 营业收入含金量
营业收入含金量=销售商品、提供劳务收到的现金/营业收入
其中:
销售商品、提供劳务收到的现金=行业所有上市公司销售商品、提供劳务收到的现金之和
营业收入=行业所有上市公司营业收入之和
上市公司的销售商品、提供劳务收到的现金可以在现金流量表中查到。
4. 应收款项占营业收入比重
应收款项占营业收入比重=应收账款/营业收入
其中:
应收账款=行业所有上市公司应收账款之和
营业收入=行业所有上市公司营业收入之和
上市公司的应收账款可以在资产负债表中查到。
SPL代码
A |
|
1 |
=file("all_stock_info.csv").import@tc(ts_code,industry,name) |
2 |
=A1.select(ts_code.split(".")(2)!="BJ") |
3 |
=file("IncomeStatement/income_statement.csv").cursor@tc() |
4 |
=A3.select(end_date%10000==1231&&end_date\10000>2013&&total_revenue&&total_cogs&&ts_code.split(".")(2)!="BJ").fetch@x() |
5 |
=A4.group(ts_code,end_date).(~.maxp(f_ann_date)).sort(ts_code,end_date) |
6 |
=file("BalanceSheet/balance_sheet.csv").cursor@tc() |
7 |
=A6.select(end_date%10000==1231&&end_date\10000>2013&&ts_code.split(".")(2)!="BJ").fetch@x() |
8 |
=A7.group(ts_code,end_date).(~.maxp(f_ann_date)).sort(ts_code,end_date) |
9 |
=file("CashFlowStatement/cash_flow.csv").cursor@tc() |
10 |
=A9.select(end_date%10000==1231&&end_date\10000>2013&&ts_code.split(".")(2)!="BJ").fetch@x() |
11 |
=A10.group(ts_code,end_date).(~.maxp(f_ann_date)).sort(ts_code,end_date) |
12 |
=A5.join(ts_code,A2:ts_code,industry) |
13 |
=join@m(A12:income,[ts_code,end_date];A8:balance,[ts_code,end_date];A11:cash,[ts_code,end_date]) |
14 |
=A13.group([income.industry,income.end_date]) |
15 |
=A14.new(income.industry,income.end_date,(icm=~.(income),icm.len()):comp_num,(rvn=icm.sum(if(revenue,revenue,total_revenue)),cos=icm.sum(if(oper_cost,oper_cost,total_cogs)),1-cos/rvn):gross_margin,(nicm=icm.sum(n_income),total_rvn=icm.sum(total_revenue),nicm/total_rvn):nprofit_margin,(csh=~.(cash),sale_cash=csh.sum(c_fr_sale_sg),sale_cash/rvn):rvn_gold,(blc=~.(balance),acc_rcv=blc.sum(accounts_receiv),acc_rcv)/rvn:acc_rcv_rate) |
16 |
=A15.group(industry) |
17 |
=A16.select(~.count(gross_margin>0.4)/~.len()>0.8) |
18 |
=A16.select(~.count(nprofit_margin>0.15)/~.len()>0.8) |
19 |
=A16.select(~.count(rvn_gold<1)/~.len()>0.8) |
20 |
=A16.select((~.count(acc_rcv_rate<0.05&&acc_rcv_rate)/~.len()>0.8)) |
A1、A2:读取股票信息数据并取沪深交易所数据。
A3:游标读取利润表数据
A4:取近十年(2014年~2023年)的沪深交易所中营业总收入和营业总成本不是null的年报数据。
因为利润表是所有上市公司每个季度的利润表,有些表还有备份,所以数据量较大,而我们只需要最近十年的沪深交易所上市公司的年报数据,全量数据都读进内存有点浪费内存,所以使用SPL的游标来读取数据。
A5:取各公司每年最新的年报数据
A6~A8和A9~A11:和A3~A5类似,区别是取资产负债表数据和现金流量表数据。
资产负债表数据如下:
现金流量表数据如下:
A12:根据ts_code关联利润表和股票信息表,增加行业列。
A13:关联三张财务报表
A14:按行业和报告期分组
A15:计算行业财务指标
A16:按行业分组
A17:筛选80%的年份毛利润率都大于40%的行业和公司
A18:筛选80%的年份净利润率都大于15%的行业和公司
A19:筛选80%的年份营业收入都小于1的行业和公司
A20:筛选80%的年份应收账款占营业收入比重都小于5%的行业和公司
SPL的连接运算是经过精心设计的,它既可以关联后增加指定列,比如A12的运算,在利润表后增加行业名,还可以同时解析多个关联关系,比如A13中同时连接利润表、资产负债表和现金流量表,因为三张表都有序,使用@m选项会自动用归并法关联,提高关联效率。
另外,能够灵活的进行连接运算,离不开SPL对结构化数据的深刻理解,比如A13的结果本质上是个序表,但是每一列的成员又都是记录,这样使后续的财务指标计算变得简单明了,而这些是SQL和Python等语言所不能的,由此可见,SPL无愧于“量化神器”的名号。