手把手教你创建一个多因子选股策略?
发布时间:2023-8-23 14:09阅读:362
多因子选股模型是量化投资中常用的一种方法。多因子选股的核心思想是:市场影响因素是多重的并且是动态的,但是总会有一些因子在一定时期内能发挥稳定的作用。因此好的因子意味着长期稳定的收入,发现高质量的因子是每位量化投资者的一致愿望。多因子模型定量刻画了股票预期收益率与股票在每个因子上的暴露程度及每个因子的预期收益率之间的关系。即个股的预期收益是由个股的因子暴露决定的,每个因子有一个因子预期收益,股票的预期收益等于每个因子暴露度与相应因子预期收益的乘积之和,再加上股票的残差收益率。多因子量化选股的原理就是基于多因子选股模型,首先根据经济金融理论或市场经验寻找可能有效的因子,然后通过对历史数据的拟合和统计分析进行验证和筛选,最后以这些因子的组合作为选股标准,买入满足这些因子的股票。
一、多因子选股模型的步骤
多因子量化选股可以分为五大步骤,分别是因子选择、因子检验、因子筛选、模型构造及检验。
1.因子选择
因子就是股票的特征,这种特征主要用来预测收益,并进行选股。因子选择是构建多因子模型的第一步,影响股票收益率的因子多种多样,下面即将给大家介绍的这个多因子策略案例就会用到以下一些因子:profitratio、单位净值、基准净值、开仓次数、平仓次数、胜率、基准年化收益、年化收益、贝塔系数、阿尔法系数、波动率、夏普比率、下方差、索提诺比率、跟踪误差、信息比率、最大回撤等。
2.因子检验
因子检验其实就是对因子和收益率存在的显著相关性进行稳定性和单调性的检验。“稳定性“,是指因子和收益率之间存在的相关关系是否稳定;而单调性检验则是使用分层回测法对因子是否具有单调性进行检验。当这一步完成之后,我们就可以确认筛选出的因子的有效性、稳定性、单调性了,而这也是检验因子过程中最重要的一步。
3.因子筛选
同类型的因子之间可能存在着较强的相关性,回归模型中的自变量之间存在高度的相关性的话,可能会使模型产生偏差或者难以估计准确,因此还需要对因子间的相关性进行检验。因子相关性可由Pearson相关系数和Spearman相关系数进行判断,而对于相关性较高的因子,则可以将这些因子进行合成,保留更加显著的因子,舍弃相对不显著的因子。
4.模型构造及检验
模型构造就是将筛选处理后的因子按照一定的权重构造成多因子选股模型。模型构造有多种方法,比如等权重法、市值加权法、IC加权法、IR法等方法来确定各因子的权重,使构造的选股模型更加有效;还可以通过添加行业权重、因子暴露、个股上下限、收益目标和风险目标等约束条件对模型进行进一步优化。
二、编写策略代码
#!/usr/bin/python
#coding:gbk
'''
策略以HS300为基础股票池,在日线下运行,20个交易日进行一次调仓,每次买入在买入备选中因子评分前10的股票,每支股票各分配当前可用资金的10%(权重可调整)
扩展数据需要在补完HS300成分股数据之后生成,本模型中扩展数据暂时使用VBA指标ATR和ADTM生成,命名为atr和adtm
'''
importpandasaspd
importnumpyasnp
importtime
importdatetime
#1.===========================================初始化部分=============================================
definit(ContextInfo):
#获取沪深300成分股
ContextInfo.s=ContextInfo.get_sector('000300.SH')
#设定基础股票池为沪深300成分股
ContextInfo.set_universe(ContextInfo.s)
#策略运行天数
ContextInfo.day=0
#持仓情况
ContextInfo.holdings={i:0foriinContextInfo.s}
#资金权重
ContextInfo.weight=[0.1]*10#设置资金分配权重
#买入点
ContextInfo.buypoint={}
#可用资金?
ContextInfo.money=ContextInfo.capital
#策略盈利
ContextInfo.profit=0
#设置交易账户
ContextInfo.accountID='testS'
#1.===========================================初始化部分=============================================
#2.===========================================周期循环部分===========================================
defhandlebar(ContextInfo):
rank1={}
rank2={}
rank_total={}
tmp_stock={}
#当前bar线索引号
d=ContextInfo.barpos
#获取过去1日的开盘价数据
price=ContextInfo.get_history_data(1,'1d','open',3)
#策略运行天数大于60天且到达20天的调仓周期
ifd>60andd ==0:
#当前bar线日期
nowDate=timetag_to_datetime(ContextInfo.get_bar_timetag(d),'%Y%m%d')
#print(nowDate)
#获取待买入、卖出股票池
buys,sells=signal(ContextInfo)
#
order={}
#获取待买入
forkinbuys.keys():
ifbuys[k]==1:
#获取待买入个股在所有品种中的atr排名
rank1[k]=ext_data_rank(
'atr',k[-2:]+k[0:6],0,ContextInfo)
#获取待买入个股在所有品种中的adtm排名
rank2[k]=ext_data_rank(
'adtm',k[-2:]+k[0:6],0,ContextInfo)
#print(rank1[k],rank2[k])
#人为设置因子的权重,此处取了0.5和-0.5
rank_total[k]=0.5*rank1[k]-0.5*rank2[k]
#对rank_total按照值value进行排序,并返回一个列表,列表里面的元素是形如(code,value)的元组
tmp=sorted(rank_total.items(),key=lambdaitem:item[1])
#print(tmp)
#如果买入备选股票数大于10只,则选取因子排序最小的10只
iflen(tmp)>=10:
tmp_stock={i[0]foriintmp[:10]}
#如果买入备选股票数小于10只,则全选
else:
tmp_stock={i[0]foriintmp}
forkinbuys.keys():
ifknotintmp_stock:
buys[k]=0
iftmp_stock:
print('买入备选股票列表:',tmp_stock)
forkinContextInfo.s:
#卖出持仓中待卖出的股票
ifContextInfo.holdings[k]>0andsells[k]==1:
print('readytosell')
#将持仓中待卖出股票以昨日收盘价清仓
order_shares(
k,-ContextInfo.holdings[k]*100,'fix',price[k][-1],ContextInfo,ContextInfo.accountID)
#计算账户可用资金(手续费按万三设定)
ContextInfo.money+=price[k][-1]*ContextInfo.holdings[k]*\
100-0.0003*\
ContextInfo.holdings[k]*100*price[k][-1]
#计算账户盈利
ContextInfo.profit+=(price[k][-1]-ContextInfo.buypoint[k])*ContextInfo.holdings[k]*\
100-0.0003*\
ContextInfo.holdings[k]*100*price[k][-1]
#print(price[k][-1])
#print(k)
#print(ContextInfo.money)
ContextInfo.holdings[k]=0
#为待买入股票等权分配资金
ContextInfo.money_distribution={
k:i*ContextInfo.moneyfor(k,i)inzip(tmp_stock,ContextInfo.weight)}
forkintmp_stock:
#买入持仓中没有的买入备选股票列表
ifContextInfo.holdings[k]==0andbuys[k]==1:
print('readytobuy')
#备选股票买入手数
order[k]=int(
ContextInfo.money_distribution[k]/(price[k][-1]))/100
#以昨日收盘价买入备选股票
order_shares(
k,order[k]*100,'fix',price[k][-1],ContextInfo,ContextInfo.accountID)
#记录买点
ContextInfo.buypoint[k]=price[k][-1]
#计算可用资金
ContextInfo.money-=price[k][-1]*order[k]*\
100-0.0003*order[k]*100*price[k][-1]
#记录盈利
ContextInfo.profit-=0.0003*\
order[k]*100*price[k][-1]
print(k)
#记录持仓中备选股票对应手数
ContextInfo.holdings[k]=order[k]
print(ContextInfo.money,ContextInfo.profit,ContextInfo.capital)
#计算利润率
profit=ContextInfo.profit/ContextInfo.capital
#进行回测则画出利润率曲线
ifnotContextInfo.do_back_test:
ContextInfo.paint('profit_ratio',profit,-1,0)
#2.===========================================周期循环部分===========================================
#3.===========================================初步删选股票池=========================================
defsignal(ContextInfo):
buy={i:0foriinContextInfo.s}
sell={i:0foriinContextInfo.s}
#获取基础股票池历史22天的日最高价
data_high=ContextInfo.get_history_data(22,'1d','high',3)
#获取基础股票池历史2天的日最高价
data_high_pre=ContextInfo.get_history_data(2,'1d','high',3)
#获取基础股票池历史62天的日收盘价
data_close60=ContextInfo.get_history_data(62,'1d','close',3)
#print(data_high)
#print(data_close)
#print(data_close60)
forkinContextInfo.s:
ifdata_close60.has_key(k):
#过去62天未出现停牌,数据齐全
iflen(data_high_pre[k])==2andlen(data_high[k])==22andlen(data_close60[k])==62:
#超过20日最高价,加入买入备选
ifdata_high_pre[k][-2]>max(data_high[k][:-2]):
buy[k]=1
#低于60日均线,加入卖出备选
elifdata_high_pre[k][-2]
sell[k]=1
#print(buy)
#print(sell)
returnbuy,sell
#3.===========================================初步删选股票池=========================================
详细介绍,如果您喜欢我的分享,记得帮忙点赞、收藏哟~~~
温馨提示:投资有风险,选择需谨慎。
![](https://static.cofool.com/licai/Home/image/counselor/read-yz.jpg)
-
投资小白必看!2025 年券商排名新出炉
2025-02-13 18:24
-
买房算房贷头疼?这个计算器,让你秒懂省钱贷款方案,不怕被坑!
2025-02-13 18:24