写在前面

本文通过决策树模型对股票盈利影响权重最大的5个因子,随后利用该5个因子通过随机森林算法构建了一个低估值的多因子策略,最终实现了年化,本文不足之处望多多指正。

1.基于决策树选取高权重因子

1.1广撒网(选取因子)构建模型

以沪深300股票池作为研究对象,选取pe、pb、净运营资本等19个特征作为模型训练的原始数据

# 基于随机森林的多因子选股策略
# 导入jqdata和技术分析工具
import jqdata
from jqlib.technical_analysis import *
#选择沪深300成分股做股票池  数据结构属性为list
stocks = get_index_stocks('000300.XSHG')
# print(len(stocks))
q = query(valuation.code,                                                            #指定获取股票的代码
          valuation.market_cap,                                                      #市值
          balance.total_current_assets- balance.total_current_liability,             #净运营资本 
          balance.total_liability- balance.total_assets,                             #净债务
          balance.total_liability/balance.equities_parent_company_owners,            #产权比率
          (balance.total_assets-balance.total_current_assets)/balance.total_assets,  #非流动资产比率  
          balance.equities_parent_company_owners/balance.total_assets,               # 股东权益比率
          indicator.inc_total_revenue_year_on_year,                                  #营收增长率 
          valuation.turnover_ratio,                                                  #换手率
          valuation.pe_ratio,                                                        #市盈率(PE)
          valuation.pb_ratio,                                                        #市净率(PB)
          valuation.ps_ratio,                                                        #市销率(PS)
          indicator.roa).filter(valuation.code.in_(stocks))                          #总资产收益率因子 
df = get_fundamentals(q, date = None)
#把数据表的字段名指定为对应的因子名
df.columns = ['code', '市值', '净营运资本', 
              '净债务', '产权比率','非流动资产比率',
              '股东权益比率', '营收增长率'
          ,'换手率','PE','PB','PS','总资产收益率']
#检查结果
#df.head()
#将股票代码作为数据表的index
df.index = df.code.values
#使用del也可以删除列
del df['code']
#下面来把时间变量都定义好
today = datetime.datetime.today()
#设定3个时间差,分别是50天,1天和2天
delta50 = datetime.timedelta(days=50)
delta1 = datetime.timedelta(days=1)
delta2 = datetime.timedelta(days=2)
#50日前作为一个历史节点
history = today - delta50
#再计算昨天和2天前的日期
yesterday = today - delta1
two_days_ago = today - delta2
#下面就获取股票的动量线、成交量、累计能量线、平均差、
#指数移动平均、移动平均、乖离率等因子
#时间范围都设为10天
df['动量线']=list(MTM(df.index, two_days_ago, 
                   timeperiod=10, unit = '1d', 
                   include_now = True, 
                   fq_ref_date = None).values())
df['成交量']=list(VOL(df.index, two_days_ago, M1=10 ,
                   unit = '1d', include_now = True, 
                   fq_ref_date = None)[0].values())
df['累计能量线']=list(OBV(df.index,check_date=two_days_ago, 
                     timeperiod=10).values())
df['平均差']=list(DMA(df.index, two_days_ago, N1 = 10, 
                   unit = '1d', include_now = True, 
                   fq_ref_date = None)[0].values())
df['指数移动平均']=list(EMA(df.index, two_days_ago, timeperiod=10, 
                      unit = '1d', include_now = True, 
                      fq_ref_date = None).values())
df['移动平均']=list(MA(df.index, two_days_ago, timeperiod=10, 
                   unit = '1d', include_now = True, 
                   fq_ref_date = None).values())
df['乖离率']=list(BIAS(df.index,two_days_ago, N1=10, 
                    unit = '1d', include_now = True, 
                    fq_ref_date = None)[0].values())
#把数据表中的空值用0来代替
df.fillna(0,inplace=True)
#检查是否成功
df.head()

随机森林预测模型因子筛选 因子模型 随机森林_随机森林

以上述特征组成的数据集作为训练的特征集X,以未来50日盈利与否作为训练的分类结果y

#获取股票前一日的收盘价
df['close1']=list(get_price(stocks, 
                       end_date=yesterday, 
                       count = 1,
                       fq='pre',panel=False)['close'])
#获取股票50日前的收盘价
df['close2']=list(get_price(stocks,  
                       end_date=history, 
                       count = 1,
                       fq ='pre',panel=False)['close'])

#计算出收益
df['return']=df['close1']/df['close2']-1
#如果收益大于平均水平,则标记为1
#否则标记为0
df['signal']=np.where(df['return']<df['return'].mean(),0,1)
#检查是否成功

#把因子值作为样本的特征,所以要去掉刚刚添加的几个字段
X = df.drop(['close1', 'close2', 'return', 'signal'], axis = 1)
#把signal作为分类标签
y = df['signal']
#将数据拆分为训练集和验证集
X_train,X_test,y_train,y_test=\
train_test_split(X,y,test_size = 0.2)

构建分类模型,查看分类准确率与各因子对股票盈利影响权重

#导入数据集拆分工具
from sklearn.model_selection import train_test_split
#导入决策树分类器
from sklearn.tree import DecisionTreeClassifier
#把因子值作为样本的特征,所以要去掉刚刚添加的几个字段
X = df.drop(['close1', 'close2', 'return', 'signal'], axis = 1)
#把signal作为分类标签
y = df['signal']
#将数据拆分为训练集和验证集
X_train,X_test,y_train,y_test=\
train_test_split(X,y,test_size = 0.2)
#创建决策树分类器实例,指定random_state便于复现
clf = DecisionTreeClassifier(random_state=1000)
#拟合训练集数据
clf.fit(X_train, y_train)
#查看分类器在训练集和验证集中的准确率
print(clf.score(X_train, y_train),
      clf.score(X_test, y_test))

# 找出比较关键的几个特征
#重要性就是决策树给出的feature_importances_
factor_weight = pd.DataFrame({'features':list(X.columns),
                             'importance':clf.feature_importances_}).sort_values(
    #这里根据重要程度降序排列,一遍遍找到重要性最高的特征
    by='importance', ascending = False)
#检查结果
factor_weight

随机森林预测模型因子筛选 因子模型 随机森林_随机森林预测模型因子筛选_02

可以看到,模型分类准确率为86.7%,选用因子对结果影响因子较大为平均差、营收增长率、非流动资产比率、股东权益比率和换手率

2.使用高权重因子选股

stocks = get_index_stocks('000300.XSHG')
q = query(valuation.code,valuation.market_cap,
                   valuation.pe_ratio,                                               #市盈率(PE)
          valuation.ps_ratio).filter(valuation.code.in_(stocks))                     #市销率(PS)     
dataset = get_fundamentals(q)
dataset['平均差'] = list(DMA(dataset.code, yesterday)[0].values())
dataset['换手率'] = list(HSL(dataset.code, yesterday)[0].values())
dataset['移动平均'] = list(MA(dataset.code, yesterday).values())
dataset['乖离率'] = list(BIAS(dataset.code, yesterday)[0].values())
dataset['动量线'] = list(MTM(dataset.code,yesterday).values())

dataset.index = dataset.code
dataset.drop('code', axis = 1, inplace = True)

from sklearn.ensemble import RandomForestRegressor
reg = RandomForestRegressor(random_state=20)
X = dataset.drop('market_cap', axis = 1)
y = dataset['market_cap']
reg.fit(X,y)

predict = pd.DataFrame(reg.predict(X), 
                       #保持和y相同的index,也就是股票的代码
                       index = y.index,
                       #设置一个列名,这个根据你个人爱好就好
                       columns = ['predict_mcap'])
#使用真实的市值,减去模型预测的市值
diff = predict['predict_mcap'] - dataset['market_cap']
#将两者的差存入一个数据表,index还是用股票的代码
diff = pd.DataFrame(diff, index = y.index, columns = ['diff'])
#将该数据表中的值,按生序进行排列
diff = diff.sort_values(by = 'diff', ascending = False)
#找到市值被低估最多的10只股票
diff[:10]

随机森林预测模型因子筛选 因子模型 随机森林_lua_03

策略回测

随机森林预测模型因子筛选 因子模型 随机森林_随机森林预测模型因子筛选_04

该策略于2019年1月到6月期间收益率为26.06%,基准收益19.45%高出6.61%,跑赢大盘,实现套利