同花顺Supermind量化交易 资金面专题1--资金单因子 附阐述和源代码

133 阅读8分钟

导读:

主流的多因子选股模型通常采用量价数据(开高低收、成交量)、财务数据(流通市值、市盈率、市净率等)、舆情数据(事件)来挖掘市场中存在的超额收益。本文主要以资金数据为落脚点,试图挖掘有效的资金因子(月频),并构建资金因子选股策略,获取市场超额收益。

1.资金数据:

个股的资金数据来自于交易时段产生的成交信息,通常反映市场上交易者的交易动向,进而反应股票的微观供求信息。考虑到短线资金数据掺杂着较多的噪音,且短期盘面受投资者情绪、事件等因素影响,本文选取17个个股资金指标,从月频角度,试图挖掘有效的资金因子。

image.png

2.月频资金因子基本挖掘逻辑:

image.png 单因子分析:了解每个资金指标的预测能力和稳定性
合成因子分析:按照金融逻辑,运用17个单因子中的若干个合成因子,并对分析合成因子的预测能力和稳定性。本文分析了“机构收割散户”和“资金反转”两个因子。
因子策略回测:将有效的合成因子构建成因子策略,在回测环境中测试。


3.单因子分析(单期:2018年1月为例):

我们获取2018年1月末股票池内所有个股的主动买入特大单金额数据,并进行累加,作为当期因子值,然后按因子值从大到小排序,切分成5组,计算5个组别在2月份的收益率,其中每个组别的收益率为该组别下所有个股的下月收益率均值。再计算该因子当期的RANK IC值,即当期所有个股因子值的RANK与下个月收益率的RANK的相关系数。

image.png

4.单因子分析(多期叠加):

按照单期计算方法,我们获取2014年至今的所有数据结果,并将5个组别的收益率进行累积,每组的初始净值为1,我们还是以主动买入特大单金额数据为例。结果分析:五组收益走势并未出现明显差异。IC均值为4.727%,因子具有一定的预测能力,IR比率为0.195,预测稳定性较差。综合分析:非优质因子。具体数据图表如下:

image.png

image.png

5.单因子分析(17个因子汇总):

上文我们已经完成了单个因子的单期和多期计算,随后我们对17个资金因子进行计算汇总,在17个资金因子中,从IC均值和IR比率分析,大单净量因子位居首位、其次为DDE大单净额和金额流入率,因子具有较强的预测能力,但资金因子的IC标准差在20%上下,使得IR不足0.5,因此单个资金因子的预测稳定性较差。图表如下:

image.png

image.png

资金面单因子

作者:陈城

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--资金单因子 附阐述和源代码