本文通过决策树模型对股票盈利影响权重最大的5个因子,随后利用该5个因子通过随机森林算法构建了一个低估值的多因子策略,最终实现了年化,本文不足之处望多多指正。
以沪深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
可以看到,模型分类准确率为86.7%,选用因子对结果影响因子较大为平均差、营收增长率、非流动资产比率、股东权益比率和换手率
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]
该策略于2019年1月到6月期间收益率为26.06%,基准收益19.45%高出6.61%,跑赢大盘,实现套利