Python量化交易学习——Part6:多因子选股策略实战(1)

本文介绍了基于成长因子的多因子选股策略,采用等权重相加的方法,利用净资产收益率和净利润增长率构建成长模型。通过计算股票的打分并排序,得出股票清单,为后续的回测做准备。
摘要由CSDN通过智能技术生成

在上一节中,我们学习了基于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)  #得到了最终的股票清单

输出结果如下:
在这里插入图片描述
在这里插入图片描述
在这里我们假设所有因子的权重相同,进行等比例加权,得到了各个成分股的最终打分,之后根据打分对成分股进行排序,得到了最终的股票清单,我们便可以根据股票清单的前几名进行股票买卖,下一节我们将采用此清单进行回测,看实际收益率效果如何。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值