01 了解皮氏F评分法
皮氏F评分法分三部分给股票打分,即
一、盈利能力
资产回报率(若本年度学位为正,为1分,否则为0分);经营性现金流(本年度度为正得1分,否则为0分);资产收益率(ROA)的变化(若本年ROA高于上年,为1分,否则为0分);应计项目(若本年度经营现金流为税后净利润,为1分;否则为0);二.安全
长期负债比率的变化(本年比率低于上年的为1分,否则为0分);流动比率的变化(本年高于上年得1分,否则为0分);股份数量的变化(无新股发行的得1分);
三.增长
毛利率变化(本年高于上年为1分,否则为0)资产周转率变化(本年高于上年为1分,否则为0)如果一家公司的皮氏F评分在8以上,则认为是值得投资的股票,否则认为是弱势。
02 以茅台为例,计算皮氏F评分
资产收益率的局限性在于不能反映银行的资本成本,净资产收益率弥补了资产收益率这一指标的不足,所以我们用股东权益收益率(ROE)[资产收益率(ROA)杠杆率(L)]
贵州茅台(600519.SH)
盈利能力ROE为31.41,所以盈利能力1(资产收益率的局限性在于不能反映银行的资本成本,净资产收益率弥补了资产收益率指标的不足)的经营性现金流为516.7亿,所以盈利能力1今年ROE(31.41)去年ROE(33.09),所以盈利能力0
yle="font-size: 0.944em;">今年度的经营现金流量(516.7亿)>税后净利(470亿),所以获利性+1- 今年的资产负债率(21.40%)<去年的负债比率(22.49%),所以安全性+1
- 今年的流动比率(4.06)>去年(3.87),所以安全性+0
- 今年未增发新股,所以安全性+1
- 今年毛利率(91.4)>去年毛利率(91.3),所以成长性+1
- 今年存货周转天数(1195)<去年存货周转天数(1182),所以成长性+0
综合以上指标,贵州茅台(600519.SH)使用皮氏F评分法的得分为6分,与我们认为较好的8差了2分,所以在使用皮氏F评分法的标准里茅台不算是优质股票.
03 编写代码,批量对股票打分
代码逻辑主要分三部分,第一部分取数,第二部分根据皮氏F评分法进行判断,第三部分将结果汇总形成数据表
编写代码前需要相关指标的数据源,此处我已整理完毕,有需求的读者可以私信我
import osimport timeimport pandas as pdfrom joblib import Parallel, delayed# 文件引用参数表from GlobalPathDictQuan import global_path_dictfrom get_stock_basic import StockBasic# 导入数据库,tu_share的key等统一配置from quantitative_config import *def init_tushare_stock_list(): # 防止key过期或网络不好等情况,此处主要捕捉tu_share的Exception try: tu_share_full_code_list = StockBasic(ts_token=ts_token).stock_basic['ts_code'].unique() except Exception as exception_at_get_tu_share_full_code_list: print(f"failed at get tu_share full code list, detail fail message:{exception_at_get_tu_share_full_code_list}") tu_share_full_code_list = [] return tu_share_full_code_listdef return_csv_data(name): days = 0 timestamp = time.strftime("%Y-%m-%d", time.localtime(time.time() - (86400 * days))) target_csv_file = os.path.join(global_path_dict[name], f"{name}_{timestamp}.csv") while not os.path.exists(target_csv_file): days += 1 timestamp = time.strftime("%Y-%m-%d", time.localtime(time.time() - (86400 * days))) target_csv_file = os.path.join(global_path_dict[name], f"{name}_{timestamp}.csv") return pd.read_csv(target_csv_file, on_bad_lines='skip', low_memory=False)def grade_stock(code): cash_data = return_csv_data('皮氏F评分所需指标-现金流') operate_data = return_csv_data('皮氏F评分所需指标') stock_cnt_data = return_csv_data('皮氏F评分所需指标-股本数') single_operate_stock_data = operate_data.loc[operate_data['标准股票代码'] == code].copy() single_cash_stock_data = cash_data.loc[cash_data['标准股票代码'] == code].copy() single_stock_cnt_data = stock_cnt_data.loc[stock_cnt_data['security_code'] == code].copy() profitability_score = 0 # 要求最少包含两年期数据,如果不足两年期数据则返回-1,符合要求进行皮氏F评分并返回分数 if single_operate_stock_data.shape[0] > 1 and single_cash_stock_data.shape[0] > 1 and single_stock_cnt_data.shape[0] > 1: roe_list = single_operate_stock_data['净资产收益率(加权)(%)'].values cash_list = single_cash_stock_data['经营活动产生的现金流量净额'].values profit_list = single_operate_stock_data['扣非净利润(元)'].values liability_list = single_operate_stock_data['资产负债率(%)'].values run_list = single_operate_stock_data['流动比率'].values gross_profit_list = single_operate_stock_data['毛利率(%)'].values turn_over_list = single_operate_stock_data['存货周转天数(天)'].values stock_cnt_list = single_stock_cnt_data['总股本(亿)'].values print(f"==============================================={code} ↓ START===============================================") # ==================================================================获利性↓================================================================== # 如果本年期ROE(净资产收益率(加权)(%)) > 0, 则获利性得分 + 1 if roe_list[0] > 0: profitability_score += 1 print(f"股票编码:{code}, 获利性得分 + 1, 目前总得分:{profitability_score},本年期ROE:{roe_list[0]} > 0") else: print(f"股票编码:{code}, 获利性得分 + 0, 目前总得分:{profitability_score},本年期ROE:{roe_list[0]} <= 0") # 如果本年期ROE(净资产收益率(加权)(%)) > 上年期ROE(上年期净资产收益率(加权)(%)), 则获利性得分 + 1 if roe_list[0] > roe_list[1]: profitability_score += 1 print(f"股票编码:{code}, 获利性得分 + 1, 目前总得分:{profitability_score},本年期ROE:{roe_list[0]} > 上年期ROE:{roe_list[1]}") else: print(f"股票编码:{code}, 获利性得分 + 0, 目前总得分:{profitability_score},本年期ROE:{roe_list[0]} <= 上年期ROE:{roe_list[1]}") # 如果本年期现金流量为正, 则获利性 + 1 if cash_list[0] > 0: profitability_score += 1 print(f"股票编码:{code}, 获利性得分 + 1, 目前总得分:{profitability_score},本年期现金流量:{cash_list[0]} > 0") else: print(f"股票编码:{code}, 获利性得分 + 0, 目前总得分:{profitability_score},本年期现金流量:{cash_list[0]} <= 0") # 如果本年期现金流量>本年期净利润, 则获利性 + 1 if cash_list[0] > profit_list[0]: profitability_score += 1 print(f"股票编码:{code}, 获利性得分 + 1, 目前总得分:{profitability_score},本年期现金流量:{cash_list[0]} > 本年期现金流量净利润:{profit_list[0]}") else: print(f"股票编码:{code}, 获利性得分 + 0, 目前总得分:{profitability_score},本年期现金流量:{cash_list[0]} <= 本年期现金流量净利润:{profit_list[0]}") # ==================================================================获利性↑================================================================== # ==================================================================安全性↓================================================================== # 如果本年期资产负债率(%) < 去年资产负债率(%), 则安全性 + 1 if liability_list[0] < liability_list[1]: profitability_score += 1 print(f"股票编码:{code}, 安全性得分 + 1, 目前总得分:{profitability_score},本年期资产负债率(%):{liability_list[0]} < 去年资产负债率(%):{liability_list[1]}") else: print(f"股票编码:{code}, 安全性得分 + 0, 目前总得分:{profitability_score},本年期资产负债率(%):{liability_list[0]} >= 去年资产负债率(%):{liability_list[1]}") # 如果本年期流动比率 > 去年流动比率, 则安全性 + 1 if run_list[0] > run_list[1]: profitability_score += 1 print(f"股票编码:{code}, 安全性得分 + 1, 目前总得分:{profitability_score},本年期流动比率:{run_list[0]} > 去年流动比率:{run_list[1]}") else: print(f"股票编码:{code}, 安全性得分 + 0, 目前总得分:{profitability_score},本年期流动比率:{run_list[0]} <= 去年流动比率:{run_list[1]}") # 如果本期总股本数 <= 上期总股本数, 则安全性 + 1 if stock_cnt_list[0] <= stock_cnt_list[1]: profitability_score += 1 print(f"股票编码:{code}, 安全性得分 + 1, 目前总得分:{profitability_score},本期总股本数:{stock_cnt_list[0]} <= 上期总股本数:{stock_cnt_list[1]}") else: print(f"股票编码:{code}, 安全性得分 + 0, 目前总得分:{profitability_score},本期总股本数:{stock_cnt_list[0]} > 上期总股本数:{stock_cnt_list[1]}") # ==================================================================安全性↑================================================================== # ==================================================================成长性↓================================================================== # 如果本年期毛利率(%) > 去年毛利率(%), 则成长性 + 1 if gross_profit_list[0] > gross_profit_list[1]: profitability_score += 1 print(f"股票编码:{code}, 成长性得分 + 1, 目前总得分:{profitability_score},本年期毛利率(%):{gross_profit_list[0]} > 去年毛利率(%):{gross_profit_list[1]}") else: print(f"股票编码:{code}, 成长性得分 + 0, 目前总得分:{profitability_score},本年期毛利率(%):{gross_profit_list[0]} <= 去年毛利率(%):{gross_profit_list[1]}") # 如果本年期存货周转天数 < 去年存货周转天数, 则成长性 + 1 if turn_over_list[0] < turn_over_list[1]: profitability_score += 1 print(f"股票编码:{code}, 成长性得分 + 1, 目前总得分:{profitability_score},本年期存货周转天数:{turn_over_list[0]} < 去年存货周转天数:{turn_over_list[1]}") else: print(f"股票编码:{code}, 成长性得分 + 0, 目前总得分:{profitability_score},本年期存货周转天数:{turn_over_list[0]} >= 去年存货周转天数:{turn_over_list[1]}") # ==================================================================成长性↑================================================================== print(f"==============================================={code} ↑ END ===============================================") return pd.DataFrame({'Code': code, 'Score': profitability_score}, index=[0]) else: return pd.DataFrame({'Code': code, 'Score': -1}, index=[0])if __name__ == "__main__": stock_list = init_tushare_stock_list() 多进程 data = pd.concat( # 这里引用的joblib的Parallel方法,以实现多进程处理,速度较单进程提升约12倍,处理器核心越多越给力 Parallel( n_jobs=-2, prefer='processes' )( delayed(grade_stock)( stock, ) for stock in stock_list ) ) data.to_excel('./score.xlsx', index=False)
针对输出的score我们进行简单的透视验证,可以看出分数呈正态分布,说明皮氏F评分是客观的:

A股所有股票评分分布柱状图
本文我们先是阐述了皮氏F评分的理论基础,又以贵州茅台举例说明了计分方式,继而使用了东方财富的API进行了4497只股票的评分,最后对分数分布进行了简单的透视.
欢迎大家留言,点赞!我是数分阿威,一只战斗在A股市场的数据分析师