在上一节中,我们学习了基于IC值的多因子计算方法,说白了就是先选择我们认为与股票收益率影响大的因子(如市盈率、净资产收益率、净利润增长率等),之后计算各个因子与股票收益率之间的相关系数,选择相关系数大的因子进行加权得到新的因子,之后求得新因子与收益率之间的相关系数,如果相关系数变大,这说明新因子的预测准确率更高,我们可以用新的因子作为我们预测的标的。
为了简化,我们先假设所有因子的相关系数都相同,相当于对所有因子进行等权重相加,看一下最终效果如何。
实战:基于成长因子的模型测试
成长模型是一种以公司未来成长为基础的选股模型。详细可以参加Philip A.Fisher(费雪)的著作《怎样选择成长股》。在实际操作中,成长型投资通常是在对经济周期和行业景气分析的基础上,结合股票基本面情况,包括收益率和增长率等属性分析,来评估和选择成长型股票。
在量化形式上,成长型投资主要是通过ROE(净资产收益率=资产净利率×权益乘数)、ROA(资产回报率= 税后净利润/总资产)、ROIC(资本回报率= (净收入 - 税收) / (股东权益+有息负债))、营业收入增长率、主营业务利润率等参数来挖掘成长性相对更高的股票。
模型说明
以资本回报率-净利润增长率两个指标来构建成长模型。根据我们对各项指标的梳理,现在将资本回报率列为质量指标,另外构建如下成长因子指标的组合因子。
成长因子 | stk_get_finance_deriv_pt()函数中对应字段名 |
---|---|
息税前利润增长率 | 息税前收益增长率=(本期息税前利润 – 上期息税前利润) / 上期息税前利润 (ebit_ps:每股息税前利润) |
净利润同比增长率 | net_prof_yoy |
营业利润同比增长率 | oper_prof_yoy |
总资产同比增长率 | ttl_asset_yoy |
经营活动产生的现金流量净额同比增长率 | net_cf_oper_yoy |
净资产同比增长率 | net_asset_yoy |
基本每股收益同比增长率 | eps_bas_yoy |
净资产收益率同比增长率(摊薄) | roe_yoy |
利润总额同比增长率 | ttl_prof_yoy |
import numpy as np
import pandas as pd
import gm.api as gm
import datetime
from dateutil.relativedelta import relativedelta
from sklearn.preprocessing import MinMaxScaler
gm.set_token("自己的token码") #输入自己的token
# 获取沪深300成分股的代码,存储到HS300_symbol_list中(list格式)
index = "SHSE.000300"
HS300_array = gm.stk_get_index_constituents(index='SHSE.000300') #获取沪深300成分股数据
"""
按照股票代码从大到小进行排序,注意下面这句非常重要,因为在实际使用过程中我发现,gm.stk_get_finance_deriv()和参数symbols=HS300_symbol_list中的顺序
并不一样,这回导致我们采用for循环中得到的参数数据和采用gm.stk_get_finance_deriv()顺序完全不同,所以我们先对股票代码进行排序,以控制for循环中返回的参数数据
之后我们再把gm.stk_get_finance_deriv()中得到的顺序同样进行从大到小排序,才能保持两组数据完全一致。
"""
HS300_array = HS300_array.sort_values(["symbol"],ascending=False) # 按照股票代码从大到小排序
HS300_symbol_array = HS300_array['symbol'].values
HS300_symbol_list = list(HS300_symbol_array) # 转换为list类型才能进行后续处理
# 采用pd.DataFrame建立二维数据表,初始化数据表,后续可以将数据存储到表中
factor_matrix = pd.DataFrame([])
factor_matrix["symbol"] = HS300_symbol_list
# factor_matrix["earnings before interest and tax"] = -999 # 息税前收益增长率,很多数据都差不到,暂时不用这个因子
factor_matrix["net_prof_yoy"] = -999 # 净利润同比增长率
factor_matrix["oper_prof_yoy"] = -999 # 营业利润同比增长率
factor_matrix["ttl_asset_yoy"] = -999 # 总资产同比增长率
factor_matrix["net_cf_oper_yoy"] = -999 # 经营活动产生的现金流量净额同比增长率
factor_matrix["net_asset_yoy"] = -999 # 净资产同比增长率
factor_matrix["eps_bas_yoy"] = -999 # 基本每股收益同比增长率
factor_matrix["roe_yoy"] = -999 # 净资产收益率同比增长率(摊薄)
factor_matrix["ttl_prof_yoy"] = -999 # 利润总额同比增长率
"""
在这里所有的值都被定义为-999,这样做的目的是在读取数据的时候,防止有数据缺失或者出错,将默认值设置成现实数据中可能遇到的最小值。
这样做的好处是在后续计算时可以自动将出错的数据的计算结果降为最差的结果,自动排除出错的数据集
后面我们也可以这样做,先批量获取数据值,之后判断数据有无缺失,如果产生缺失,就逐个获取对应因子的数据,对于缺失的因子数据,采用-999进行填充
"""
day_time,hour_and_mins = str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')).split(" ") # 调用datetime函数获取最新时间
six_months_ago = datetime.datetime.now() - relativedelta(months=12) # 获取9个月前的时间作为后续查询数据的起始时间(主要是息税前收益增长率的计算需要T-1的数据,所有这里设置需要大于6个月)
last_day_time,last_hour_and_mins = str(six_months_ago.strftime('%Y-%m-%d %H:%M:%S')).split(" ") # 转换时间格式到str
# 求息税前收益增长率,这个没有现成的公式,需要手动计算,计算公式为息税前收益增长率=(本期息税前利润 – 上期息税前利润) / 上期息税前利润 × 100%
# 采用dataframe格式获取数据,因为有一些数据无法获取到,所以运行起来特别慢,先注释掉,不采用这个参数
"""
for number in range(len(HS300_symbol_list)):
try:
EBIT = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="ebit_ps", start_date=last_day_time, end_date=day_time, df=True) #这里采用的是获取单个股票的数据
now_EBIT = EBIT.loc[len(EBIT)-1,["ebit_ps"]]
last_EBIT = EBIT.loc[len(EBIT)-2,["ebit_ps"]]
EBITG = ((now_EBIT-last_EBIT)/last_EBIT)["ebit_ps"] # 计算后取列表中真正的float值,如果不增加后面的索引,对象值为object
factor_matrix.loc[number,["earnings before interest and tax"]] = EBITG
except:
factor_matrix.loc[number,["earnings before interest and tax"]] = -999
print(factor_matrix)
"""
#获取净利润同比增长率数据
NPY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list,fields="net_prof_yoy",date=day_time,df=True)
NPY = NPY.sort_values(["symbol"],ascending=False) #返回值symbol顺序已经被打乱,需要重新排序
NPY = NPY.reset_index(drop=True) # 需要重置索引值,否则在下方相等的时候会按照原有的索引值进行对等,顺序还是乱的
if len(NPY) == len(HS300_symbol_list):
factor_matrix["net_prof_yoy"] = NPY["net_prof_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
NPY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="net_prof_yoy",start_date=last_day_time, end_date=day_time)
NPY_value = NPY[-1]["net_prof_yoy"]
factor_matrix.loc[number, ["net_prof_yoy"]] = NPY_value
except:
factor_matrix.loc[number,["net_prof_yoy"]] = -999
# # 获取营业利润同比增长率数据
OPY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="oper_prof_yoy", date=day_time, df=True)
OPY = OPY.sort_values(["symbol"],ascending=False)
OPY = OPY.reset_index(drop=True)
if len(OPY) == len(HS300_symbol_list):
factor_matrix["oper_prof_yoy"] = OPY["oper_prof_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
OPY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="oper_prof_yoy",start_date=last_day_time, end_date=day_time)
OPY_value = OPY[-1]["oper_prof_yoy"]
factor_matrix.loc[number, ["oper_prof_yoy"]] = OPY_value
except:
factor_matrix.loc[number, ["oper_prof_yoy"]] = -999
# # 获取总资产同比增长率数据
TAY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="ttl_asset_yoy", date=day_time, df=True)
TAY = TAY.sort_values(["symbol"],ascending=False)
TAY = TAY.reset_index(drop=True)
if len(TAY) == len(HS300_symbol_list):
factor_matrix["ttl_asset_yoy"] = TAY["ttl_asset_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
TAY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="ttl_asset_yoy",start_date=last_day_time, end_date=day_time)
TAY_value = TAY[-1]["ttl_asset_yoy"]
factor_matrix.loc[number, ["ttl_asset_yoy"]] = TAY_value
except:
factor_matrix.loc[number, ["ttl_asset_yoy"]] = -999
# 获取经营活动产生的现金流量净额同比增长率数据
NCOY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="net_cf_oper_yoy", date=day_time, df=True)
NCOY = NCOY.sort_values(["symbol"],ascending=False)
NCOY = NCOY.reset_index(drop=True)
if len(NCOY) == len(HS300_symbol_list):
factor_matrix["net_cf_oper_yoy"] = NCOY["net_cf_oper_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
NCOY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="net_cf_oper_yoy",start_date=last_day_time, end_date=day_time)
NCOY_value = NCOY[-1]["net_cf_oper_yoy"]
factor_matrix.loc[number, ["net_cf_oper_yoy"]] = NCOY_value
except:
factor_matrix.loc[number, ["net_cf_oper_yoy"]] = -999
# 获取净资产同比增长率数据
NAY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="net_asset_yoy", date=day_time, df=True)
NAY = NAY.sort_values(["symbol"],ascending=False)
NAY = NAY.reset_index(drop=True)
if len(NAY) == len(HS300_symbol_list):
factor_matrix["net_asset_yoy"] = NAY["net_asset_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
NAY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="net_asset_yoy",start_date=last_day_time, end_date=day_time)
NAY_value = NAY[-1]["net_asset_yoy"]
factor_matrix.loc[number, ["net_asset_yoy"]] = NAY_value
except:
factor_matrix.loc[number, ["net_asset_yoy"]] = -999
# 获取基本每股收益同比增长率数据
EBY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="eps_bas_yoy", date=day_time, df=True)
EBY = EBY.sort_values(["symbol"],ascending=False)
EBY = EBY.reset_index(drop=True)
if len(EBY) == len(HS300_symbol_list):
factor_matrix["eps_bas_yoy"] = EBY["eps_bas_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
EBY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="eps_bas_yoy",start_date=last_day_time, end_date=day_time)
EBY_value = EBY[-1]["eps_bas_yoy"]
factor_matrix.loc[number, ["eps_bas_yoy"]] = EBY_value
except:
factor_matrix.loc[number, ["eps_bas_yoy"]] = -999
# 获取净资产收益率同比增长率(摊薄)数据
ROEY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="roe_yoy", date=day_time, df=True)
ROEY = ROEY.sort_values(["symbol"],ascending=False)
ROEY = ROEY.reset_index(drop=True)
if len(ROEY) == len(HS300_symbol_list):
factor_matrix["roe_yoy"] = ROEY["roe_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
ROEY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="roe_yoy",start_date=last_day_time, end_date=day_time)
ROEY_value = ROEY[-1]["roe_yoy"]
factor_matrix.loc[number, ["roe_yoy"]] = ROEY_value
except:
factor_matrix.loc[number, ["roe_yoy"]] = -999
# 获取利润总额同比增长率数据
TPY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="ttl_prof_yoy", date=day_time, df=True)
TPY = TPY.sort_values(["symbol"],ascending=False)
TPY = TPY.reset_index(drop=True)
if len(TPY) == len(HS300_symbol_list):
factor_matrix["ttl_prof_yoy"] = TPY["ttl_prof_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
TPY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="ttl_prof_yoy",start_date=last_day_time, end_date=day_time)
TPY_value = TPY[-1]["ttl_prof_yoy"]
factor_matrix.loc[number, ["ttl_prof_yoy"]] = TPY_value
except:
factor_matrix.loc[number, ["ttl_prof_yoy"]] = -999
"""
# 测试代码,与excel表中数据进行对比,看是否有误
test = gm.stk_get_finance_deriv(symbol="SZSE.301269", fields="eps_bas_yoy",start_date=last_day_time, end_date=day_time)
test_value = test[-1]["eps_bas_yoy"]
print(test_value)
"""
#从dataframe中提取数据矩阵
factor_matrix = factor_matrix.dropna() #删除包含NAN的非数据行,如果这一行中存在一个NAN,那么整行都会被删除
factor_matrix_useful = factor_matrix.iloc[:,1:] # 第0列数据是股票代码,不是因子没有用,因此从第1列开始
factor_matrix_useful = np.asmatrix(factor_matrix_useful) # 转换为矩阵
print(type(factor_matrix_useful))
#先进行列归一化,然后对每行进行标准化处理
def min_max_scaling(data):
min_val = np.min(data)
max_val = np.max(data)
return (data - min_val) / (max_val - min_val)
factor_matrix_useful = min_max_scaling(factor_matrix_useful)
weight = [[1],[1],[1],[1],[1],[1],[1],[1]]
weight_mat = np.asmatrix(weight)
res = np.dot(factor_matrix_useful,weight_mat)
factor_matrix["score"] = res
factor_matrix = factor_matrix.sort_values(["score"],ascending=False)
factor_matrix = factor_matrix.reset_index(drop=True)
factor_matrix.to_csv("factor_matrix.csv")
symbol_list = []
for value in factor_matrix["symbol"].values:
symbol_list.append(value)
print(symbol_list) #得到了最终的股票清单
输出结果如下:
在这里我们假设所有因子的权重相同,进行等比例加权,得到了各个成分股的最终打分,之后根据打分对成分股进行排序,得到了最终的股票清单,我们便可以根据股票清单的前几名进行股票买卖,下一节我们将采用此清单进行回测,看实际收益率效果如何。