导读:
主流的多因子选股模型通常采用量价数据(开高低收、成交量)、财务数据(流通市值、市盈率、市净率等)、舆情数据(事件)来挖掘市场中存在的超额收益。本文主要以资金数据为落脚点,试图挖掘有效的资金因子(月频),并构建资金因子选股策略,获取市场超额收益。
1.资金数据:
个股的资金数据来自于交易时段产生的成交信息,通常反映市场上交易者的交易动向,进而反应股票的微观供求信息。考虑到短线资金数据掺杂着较多的噪音,且短期盘面受投资者情绪、事件等因素影响,本文选取17个个股资金指标,从月频角度,试图挖掘有效的资金因子。
2.月频资金因子基本挖掘逻辑:
单因子分析:了解每个资金指标的预测能力和稳定性
合成因子分析:按照金融逻辑,运用17个单因子中的若干个合成因子,并对分析合成因子的预测能力和稳定性。本文分析了“机构收割散户”和“资金反转”两个因子。
因子策略回测:将有效的合成因子构建成因子策略,在回测环境中测试。
3.单因子分析(单期:2018年1月为例):
我们获取2018年1月末股票池内所有个股的主动买入特大单金额数据,并进行累加,作为当期因子值,然后按因子值从大到小排序,切分成5组,计算5个组别在2月份的收益率,其中每个组别的收益率为该组别下所有个股的下月收益率均值。再计算该因子当期的RANK IC值,即当期所有个股因子值的RANK与下个月收益率的RANK的相关系数。
4.单因子分析(多期叠加):
按照单期计算方法,我们获取2014年至今的所有数据结果,并将5个组别的收益率进行累积,每组的初始净值为1,我们还是以主动买入特大单金额数据为例。结果分析:五组收益走势并未出现明显差异。IC均值为4.727%,因子具有一定的预测能力,IR比率为0.195,预测稳定性较差。综合分析:非优质因子。具体数据图表如下:
5.单因子分析(17个因子汇总):
上文我们已经完成了单个因子的单期和多期计算,随后我们对17个资金因子进行计算汇总,在17个资金因子中,从IC均值和IR比率分析,大单净量因子位居首位、其次为DDE大单净额和金额流入率,因子具有较强的预测能力,但资金因子的IC标准差在20%上下,使得IR不足0.5,因此单个资金因子的预测稳定性较差。图表如下:
资金面单因子
作者:陈城
1.研究时间
从2014年1月1日至2019年1月15日,按一个月作为单位研究窗口
2.分析对象为
我们选取了17个因子作为研究对象,展示如下:
分析对象1 | 分析对象2 | 分析对象3 | 分析对象4 |
---|---|---|---|
主动买入特大单金额(元) | 被动买入特大单金额(元) | 主动买入大单金额(元) | 被动买入大单金额(元) |
主动买入中单金额(元) | 被动买入中单金额(元) | 主动卖出特大单金额(元) | 被动卖出特大单金额(元) |
主动卖出大单金额(元) | 被动卖出大单金额(元) | 主动卖出中单金额(元) | 被动卖出中单金额(元) |
小单买入金额(元) | 小单卖出金额(元) | DDE大单净额(元) | 金额流入率(%) |
大单净量(元) | --- | -- | --- |
3.单因子研究示例
我们分别从IC序列均值, IC序列标准差, IR比率,IC>0 占比, IC>3% 占比 %, IC<-3% 占比 %, |IC|>3% 占比 % 多个维度对单因子进行研究,并以因子收益为研究方向,将股票组合按因子大小分为5组。
In [18]:
'''
资金单因子因子挖掘框架
1.基本信息设置
'''
startdate = "20140101"
enddate = "20190115"
indexcode = "000016.SH"
'''
2.日历与月历列表获取
'''
tradelist = list(get_trade_days(startdate, enddate, count=None).strftime('%Y%m%d'))
#获取月度交易日列表(每月末)
mtradelist = []
for s in range(0,len(tradelist)):
if s == len(tradelist)-1:
break
if (tradelist[s+1][4:6]>tradelist[s][4:6]) or (tradelist[s+1][4:6]=='01' and tradelist[s][4:6]=='12'):
mtradelist.append(tradelist[s])
'''
3.因子收益
'''
#IC监控项
columns = ['IC序列均值 %','IC序列标准差 %','IR比率','IC>0 占比 %','IC>3% 占比 %','IC<-3% 占比 %','|IC|>3% 占比 %']
#创建DataFrame
LDdf = pd.DataFrame(columns = columns)
for factorname in ['sell_l','buy_l','act_buy_xl','pas_buy_xl','act_buy_l','pas_buy_l','act_buy_m','pas_buy_m','act_sell_xl','pas_sell_xl','act_sell_l','pas_sell_l','act_sell_m','pas_sell_m','dde_l','net_flow_rate','l_net_value']:
import pandas as pd
import numpy as np
columns = ['before 20%','20%-40%','40%-60%','60%-80%','finally 20%']
refdf = pd.DataFrame(columns = columns)
icdf = pd.DataFrame(columns = ['IC'])
for t in [s for s in range(0,len(mtradelist)-1)]:
daytime = mtradelist[t]
nextdaytime = mtradelist[t+1]
#获取当日沪深300指数成份股
stocklist = get_index_stocks(indexcode, daytime)
#剔除ST和停牌股票
is_st = get_price(stocklist, None, daytime, '1d', ['is_st', 'is_paused'], True, None, 1, is_panel=1)['is_st'].T
stock1 = list(is_st[is_st[list(is_st.columns)[0]]==0].index)
is_paused = get_price(stocklist, None, daytime, '1d', ['is_st', 'is_paused'], True, None, 1, is_panel=1)['is_paused'].T
stock2 = list(is_paused[is_paused[list(is_paused.columns)[0]]==0].index)
stock = list(set(stock1)&set(stock2))
#获取所有样本股下月涨跌幅
df = get_price(stock, daytime, nextdaytime, '1d', ['quote_rate'],is_panel=1)['quote_rate']
#计算月涨跌幅
quote_rate = df.iloc[1:].sum()/100
#整理成DataFrame格式
df = pd.DataFrame(list(quote_rate),columns=['quote_rate'],index=quote_rate.index)
#添加当月股票因子值
name = factorname
factordf = get_money_flow_step(stock,
None,
daytime,
'1d',
[name],
20,
is_panel=1)[name]
factordf = factordf.fillna(0)
factordf = factordf.rank(axis=1,ascending = False)
df[name]=list(factordf.iloc[:].sum())
#当期因子RANK值从小到大排序
df = df.sort_values(by = name,ascending = True)
#设置五组,前20%为一组,后20%为一组,中间三组,并计算每组在下月平均收益率
refstock1 = round(np.mean(list(df.iloc[:int(len(stock)*0.2)]['quote_rate'])),4)
refstock2 = round(np.mean(list(df.iloc[int(len(stock)*0.2):int(len(stock)*0.4)]['quote_rate'])),4)
refstock3 = round(np.mean(list(df.iloc[int(len(stock)*0.4):int(len(stock)*0.6)]['quote_rate'])),4)
refstock4 = round(np.mean(list(df.iloc[int(len(stock)*0.6):int(len(stock)*0.8)]['quote_rate'])),4)
refstock5 = round(np.mean(list(df.iloc[int(len(stock)*0.8):]['quote_rate'])),4)
#计算RANK-IC值
IC = round(np.corrcoef(df[name].rank(axis=0,ascending = True),df['quote_rate'].rank(axis=0,ascending = False))[0,1],3)
#录入
refdf.loc[daytime] = [refstock1,refstock2,refstock3,refstock4,refstock5]
icdf.loc[daytime] = [IC]
#净值计算
refdf = refdf+1
from operator import mul
from functools import reduce
#添加日期
refdf['day'] = refdf.index
#累积收益计算
refsumdf = pd.DataFrame()
refsumdf['前20%组累积收益'] = refdf['day'].apply(lambda x:reduce(mul,list(refdf['before 20%'])[:list(refdf['day']).index(x)+1]))
refsumdf['20%-40%组累积收益'] = refdf['day'].apply(lambda x:reduce(mul,list(refdf['20%-40%'])[:list(refdf['day']).index(x)+1]))
refsumdf['40%-60%组累积收益'] = refdf['day'].apply(lambda x:reduce(mul,list(refdf['40%-60%'])[:list(refdf['day']).index(x)+1]))
refsumdf['60%-80%组累积收益'] = refdf['day'].apply(lambda x:reduce(mul,list(refdf['60%-80%'])[:list(refdf['day']).index(x)+1]))
refsumdf['后20%组累积收益'] = refdf['day'].apply(lambda x:reduce(mul,list(refdf['finally 20%'])[:list(refdf['day']).index(x)+1]))
#可视化
import matplotlib.pyplot as plt
plt.style.use('seaborn')
import pandas as pd
import numpy as np
columns = list(refsumdf.columns)
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 2, 1]) #插入面板
color = ['tomato','green','r','b','pink']
for c in range(0,len(columns)):
x1_list=list(refsumdf[columns[c]])
y=np.array(x1_list)
x=np.array(range(0,len(x1_list)))
axes.plot(x, y, color[c])
axes.set_xlabel('Time',fontsize=15)
axes.set_ylabel('net value',fontsize=15)
axes.set_title('return of {}'.format(factorname),fontsize=20)
columns = ['before 20%','20%-40%','40%-60%','60%-80%','finally 20%']
axes.legend(columns)
#设置X轴
numlist=[]
for s in list(range(0,len(mtradelist),6)):
numlist.append(mtradelist[s])
axes.set_xticks(list(range(0,len(mtradelist),6)))
axes.set_xticklabels(numlist, fontsize=10)
#累积IC计算
icdf['day'] = icdf.index
icdf['IC累计'] = icdf['day'].apply(lambda x:np.sum(list(icdf['IC'])[:list(icdf['day']).index(x)+1]))
del icdf['day']
#可视化
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 2, 1]) #插入面板
x1_list=list(icdf['IC'])
y=np.array(x1_list)
x=np.array(range(0,len(x1_list)))
axes.plot(x, y, 'green')
axes.set_xlabel('Time',fontsize=15)
axes.set_ylabel('value',fontsize=15)
axes.set_title('value of IC',fontsize=20)
#设置X轴
numlist=[]
for s in list(range(0,len(mtradelist),6)):
numlist.append(mtradelist[s])
axes.set_xticks(list(range(0,len(mtradelist),6)))
axes.set_xticklabels(numlist, fontsize=10)
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 2, 1]) #插入面板
x1_list=list(icdf['IC累计'])
y=np.array(x1_list)
x=np.array(range(0,len(x1_list)))
axes.plot(x, y, 'tomato')
axes.set_xlabel('Time',fontsize=15)
axes.set_ylabel('value',fontsize=15)
axes.set_title('Accumulated value of IC',fontsize=20)
#设置X轴
numlist=[]
for s in list(range(0,len(mtradelist),6)):
numlist.append(mtradelist[s])
axes.set_xticks(list(range(0,len(mtradelist),6)))
axes.set_xticklabels(numlist, fontsize=10)
#IC序列均值
icmean = icdf.mean().IC*100
#IC序列标准差
icstd = np.std(list(icdf['IC']), ddof=1)*100
#IR比率
IR = abs(icmean/icstd)
#IC>0 占比
icz = len(list(icdf[icdf['IC']>0].index))/len(list(icdf.index))*100
#IC>0.03 占比
icze = len(list(icdf[icdf['IC']>0.03].index))/len(list(icdf.index))*100
#IC<-0.03 占比
iczr = len(list(icdf[icdf['IC']<-0.03].index))/len(list(icdf.index))*100
#|IC|>0.03 占比
icz2 = len(list(icdf[abs(icdf['IC'])>0.03].index))/len(list(icdf.index))*100
#录入
LDdf.loc[factorname]=[round(icmean,3),round(icstd,3),round(IR,3),round(icz,3),round(icze,3),round(iczr,3),round(icz2,3)]
LDdf
查看以上策略详情请到supermind量化交易官网查看:同花顺Supermind量化交易 资金面专题1--资金单因子 附阐述和源代码