多因子策略之冗余因子

引言:

上一篇文章《多因子选股之有效因子》,我们讲到有效因子的检验。在选择了有效因子之后,我们还需要进行一步去除冗余因子。

不同的选股因子可能由于内在的驱动因素大致相同等原因,所选出的组合在个股构成和收益等方面具有较高的一致性,因此其中的一些因子需要作为冗余因子剔除, 而只保留同类因子中收益最好,区分度最高的一个因子。例如成交量指标和流通量指标之间具有比较明显的相关性。流通盘越大的,成交量一般也会比较大,因此在选股模型中,这两个因子只选择其中一个。

主要步骤:

我参考了网络上不同的剔除方法,但都仅仅浅尝辄止,没有进行深入的代码探究。我们先说剔除冗余因子的主要步骤:

假设现有k个因子,回测周期可分为m个月,股票可均分为n个组合,

1.分别按不同因子的大小进行排序,讲股票分为n个组合进行打分,分值与该组合在整个模型形成期的收益相关,收益越大,分值越高分值赋给每月该组合内的所有个股。

如果组合10的收益大于组合1,那么就将组合 i 各个股票的各个股票分值设置为 i ,各个组合的分值从低到高进行排列分别是 1,2,3,4,5,6,7,8,9,10。如果组合1的收益大于组合10,那么正好是反过来 10,9,8,7,6,5,4,3,2,1。

2.按月计算个股的不同因子得分间的相关性矩阵

3.在计算完每月因子得分相关性矩阵后,计算整个样本期内相关性矩阵的平均值

4.设定一个得分相关性阀值,将得分相关性平均值矩阵中大于该阀值的元素所对应的因子只保留与其他因子相关性较小、有效性更强的因子,而其它因子则作为冗余因子剔除。

测试参数:

我们根据步骤,在掘金平台上实现

测试参数声明:

测试平台:掘金量化

测试时间:2016-01-01——2018-01-01

测试股票池:“上证50”成分股

测试因子:

测试步骤:

  1. 获取每个月的首个交易日,月初获取每只股票的因子数据及当月收益率,根据因子数值排序,将股票分为10组,根据平均收益为股票组合打分,分值赋予对应组合中所有股票。
  2. 分别测试股票关于四种因子的分值,根据每月结果,计算相关性系数矩阵。
  3. 计算整个测试周期的相关性系数平均值。
  4. 设定阈值,剔除冗余因子。

结果:

我们看几个月份的相关性系数矩阵

 

再来看各个月的统计表格

我们经过统计,得出平均相关性系数矩阵

结论:

我们选取的四个有效因子,平均相关性系数均小于0.25,也就是我们选取的因子相关性很低,我们可以放心的进行因子间的组合,构建新的因子。

冗余因子剔除的思想:计算股票根据每个因子的所获得的分值,通过比较该分值,从而得到因子间的相关性,相关性高的,剔除其中一个,留下有效性高并与其他因子相关性低的因子。

附:冗余因子剔除源码

from __future__ import print_function, absolute_import, unicode_literals
import seaborn as sns
import pandas as pd
from gm.api import *
import matplotlib.pyplot as plt
set_token('c395247a76e8a5caeee699d668d6f550213bc418')

#相关性矩阵热力图
def cor(df):
    dfData = df.corr()
    plt.subplots(figsize=(9, 9)) # 设置画面大小
    sns.heatmap(dfData, annot=True, vmax=1, square=True, cmap="Blues")
    plt.savefig('./BluesStateRelation.png')
    plt.show()

#获取每月第一个交易日
time=get_trading_dates(exchange='SHSE', start_date='2016-01-01', end_date='2018-01-01')
date=[]
for i in time:
    n=time.index(i)
    if i[5:7]!=time[n-1][5:7]:
        date.append(i)

#测试因子间相关性
for tradedate in date:
    data=pd.DataFrame()
    fin=pd.DataFrame()
    a=0
    for factor in ['PB','ROEANNUAL','TAGRT','SHTLIABTOTLIABRT']:
            n=date.index(tradedate)
            if n ==23:
                break
            # 获取上一个交易日
            last_day = get_previous_trading_date(exchange='SHSE', date=tradedate)
            nextmoth=date[n+1]
            # 获取上证50成份股
            stock300 = get_history_constituents(index='SHSE.000016', start_date=last_day,
                                                end_date=last_day)[0]['constituents'].keys()
            # 获取当天有交易的股票
            not_suspended_info = get_history_instruments(symbols=stock300, start_date=tradedate, end_date=tradedate)
            not_suspended_symbols = [item['symbol'] for item in not_suspended_info if not item['is_suspended']]
            #获取因子数据
            if factor=='PB':
                fin = get_fundamentals(table='trading_derivative_indicator', symbols=not_suspended_symbols,
                                       start_date=tradedate, end_date=tradedate, fields=factor,
                                       filter='', order_by='-' + factor, df=True)
            else:
                fin = get_fundamentals(table='deriv_finance_indicator', symbols=not_suspended_symbols,
                                   start_date=tradedate, end_date=tradedate, fields=factor,
                                   filter='', order_by='-'+factor, df=True)
            long=len(fin)
            fin=fin.fillna(0)
            portfolio=[]

            #获取股票收益率
            for i in fin.symbol:
                try :
                    last=history(symbol=i, frequency='1d', start_time=nextmoth, end_time=nextmoth, fields='close', skip_suspended=True,
                    fill_missing=None, adjust=ADJUST_NONE, adjust_end_time='', df=False)[0]['close']
                except:
                    portfolio.append(0)
                    continue
                pre=history(symbol=i, frequency='1d', start_time=tradedate, end_time=tradedate, fields='close', skip_suspended=True,
                    fill_missing=None, adjust=ADJUST_NONE, adjust_end_time='', df=False)[0]['close']
                portfolio.append((last - pre) / pre)
            #根据收益率对股票进行打分
            portfolio_2=[(sum(portfolio[0:4]))/5]*5+[(sum(portfolio[5:9]))/5]*5+[(sum(portfolio[10:14]))/5]*5+[(sum(portfolio[15:19]))/5]*5+[(sum(portfolio[20:24]))/5]*5+[(sum(portfolio[25:29]))/5]*5+[(sum(portfolio[30:34]))/5]*5+[(sum(portfolio[35:39]))/5]*5+[(sum(portfolio[40:44]))/5]*5+[(sum(portfolio[45-long:0]))/5]*(long-45)
            fin['portfolio_2'] = portfolio_2
            fin=fin.sort_values(by = 'portfolio_2',axis = 0,ascending = False)
            score=[10]*5+[9]*5+[8]*5+[7]*5+[6]*5+[5]*5+[4]*5+[3]*5+[2]*5+[1]*(long-45)
            fin[factor + 'score'] =score
            del fin['pub_date'],fin['end_date'],fin['portfolio_2'],fin[factor]
            a=a+1
            #获取关于四个因子下,每个股票所获得的分数值
            if a==1:
                data=fin
            else:
                data = pd.merge(data, fin, how='right', on='symbol')
    #统计分析
    del data['symbol']
    cor(data)
    print(data.corr())

作者: 宋瑞笛      来源:掘金量化  myquant.cn

推荐阅读: | 量化交易  | 期货模拟交易 | python量化交易 | 股票数据  | 量化交易策略 | 机器学习算法  | 多因子选股 |  

                   | 双均线策略 |  网格交易法 |  海龟交易法  |  跨期套利  | 行业轮动  | 指数增强  | 跨品种套利 | 日内交易 | 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值