研报复制(四):基于Logistic回归的大小盘轮动

2019-08-29 13:21:11 浏览数 (2)

说明

上一篇提到,一般有两种方式构造大小盘轮动策略:

一种是从技术面出发,基于量价指标建立模型,典型的比如上篇复制的基于相对强弱指标的大小盘轮动策略

另一种是从基本面出发,考虑影响股票回报的各项因素,以此为基础构建模型。

本文属于第二种,文章主要参考报告包括:

《国泰君安-风格投资IV:A股大小盘风格轮动研究-11112》、《20161202-申万宏源-申万宏源兼谈FOF构建中的市场风格识别:解密大小盘风格轮动之logistic模型》、《安信证券-四维视角下的大小盘风格轮动-因子法》。

报告、数据和代码后台回复“代码”获取。

02

股价影响因素

[1]中将影响股票回报率的因素分为估值因素、动量因素、货币因素、经济增长因素四大类,我们结合三篇报告,最终使用如下各项因子进行建模:

共计13项指标,数据来自WIND,提取数据代码略。各项因子的分析不再说明,参见报告。

由于数据披露的滞后性,为了防止出现未来信息,回测时对各项因子进行滞后处理。比如滞后两期的意思是,当期为10月时,使用8月份的数据。

各因子相关性如图。

03

基于Logistic回归模型的大小盘轮动

基本面数据与量价数据的最大不同在于,量价因子的及时性高但持续性弱,基本面数据披露有一定滞后性,响应没有量价因子那么迅速强烈,但持续时间较长。因此回测时采用月频调仓

Logistic回归模型的原理不具体说明,python中可以直接通过函数sklearn.LogisticRegression完成。

总体思路为,通过logistic模型预测未来市场是大盘走强还是小盘走强,因变量Y用小盘指数收益率和大盘指数收益率的差值定义,Y = 1 若差值大于0,表明小盘走强;Y = 1 若差值小于0,表明大盘走强。

每一期末,利用历史数据预测下一期Y为0还是1,为1则下一期配置小盘指数,为0则下一期配置大盘指数。

采用这种策略的收益上下界可以预期,分别对应模型预测准确率100%,0%的情况,分别对应图中蓝色橙色曲线。

04

轮动策略1

回测区间:2005年5月-2018年7月

大盘指数:HS300(000300.SH)

小盘指数:ZZ500(000905.SH)

使用所有13个因子作为自变量,建立Logistic模型,代码如下

代码语言:javascript复制
def backtest(j,datause):
    datause['predict'] = np.nan
    for i in range(5,datause.shape[0]):
        X = datause.iloc[max(0,i - j):i,1:14].values
        Y = datause.iloc[max(0,i - j):i,14].values    
        X_pred = datause.iloc[i,1:14].values.reshape(1, -1)
        clf = LogisticRegression(random_state=0, solver='lbfgs').fit(X, Y)
        datause.loc[i,'predict'] = clf.predict(X_pred)[0]  
    flags = datause[['ym','predict','next_Y']].dropna()
    flags['next_ym'] = flags.ym.apply(lambda x: x   1 if x0 < 12 else (x//100   1)*100 1)
    result = pd.merge(Indexprice,flags,left_on = 'ym',right_on = 'next_ym')    
    result['net_zz'] = result['ZZ500']/result['ZZ500'][0]
    result['net_hs'] = result['hs300']/result['hs300'][0]
    result['ret_zz'] = result['ZZ500'].pct_change(1).fillna(0)
    result['ret_hs'] = result['hs300'].pct_change(1).fillna(0)
    result['strategy_ret'] = result.ret_zz*(result.predict ==1)   result.ret_hs*(result.predict == 0)
    result['net_strategy'] = (result.strategy_ret   1).cumprod()

    return result

其中,j为用来训练模型的数据期数。如果每次使用过去所有数据训练模型,结果如下

明显优于大盘指数,但不如小盘指数。

考虑滚动的方式,每次只使用过去j期的数据,我们对j从10-100进行循环计算每个参数下的策略净值和预测准确率,结果如下

j = 20时,预测准确率62.19%,策略净值2.28

代码语言:javascript复制
result20 = backtest(20,datause)
X = np.arange(result20.shape[0])
xticklabel = result20.stockdate
xticks = np.arange(0,result20.shape[0],np.int((result10.shape[0] 1)/7))

plt.figure(figsize = [20,5])
SP = plt.axes()      
SP.plot(X,result20['net_strategy'],label = 'net_strategy',color = 'deepskyblue',linewidth = 3)  
SP.plot(X,result20['net_zz'],label = 'net_zz',color = 'cornflowerblue',linewidth = 2)   
SP.plot(X,result20['net_hs'],label = 'net_hs',color = 'darkred',linewidth = 2) 


SP.set_xticks(xticks)
SP.set_xticklabels(xticklabel[xticks],size = 20)
plt.legend()
plt.show()

05

轮动策略2

策略2出发点为,不同因素在不同时刻对于股价的影响不尽相同,因此建立Logistic模型时,考虑只使用与当期所用数据中因变量相关性最高(相关系数绝对值最大)的5个因子,其余同策略1。

代码语言:javascript复制
# j 训练模型使用的数据长度
def backtest2(j,datause):

    datause['predict'] = np.nan
    for i in range(5,datause.shape[0]):
        col = pd.DataFrame(datause[max(0,i - j):i - 1].corr()['Y'][1:14].abs()).sort_values('Y',ascending = False)[:5].index.tolist()
        X = datause.loc[max(0,i - j):i - 1,col].values
        
        Y = datause.loc[max(0,i - j):i - 1,'Y'].values    
        X_pred = datause.loc[i,col].values.reshape(1, -1)
        clf = LogisticRegression(random_state=0, solver='lbfgs').fit(X, Y)
        datause.loc[i ,'predict'] = clf.predict(X_pred)[0]  
    flags = datause[['ym','predict','next_Y']].dropna()
    flags['next_ym'] = flags.ym.apply(lambda x: x   1 if x0 < 12 else (x//100   1)*100 1)
    result = pd.merge(Indexprice,flags,left_on = 'ym',right_on = 'next_ym')    
    result['net_zz'] = result['ZZ500']/result['ZZ500'][0]
    result['net_hs'] = result['hs300']/result['hs300'][0]
    result['ret_zz'] = result['ZZ500'].pct_change(1).fillna(0)
    result['ret_hs'] = result['hs300'].pct_change(1).fillna(0)
    result['strategy_ret'] = result.ret_zz*(result.predict ==1)   result.ret_hs*(result.predict == 0)
    result['net_strategy'] = (result.strategy_ret   1).cumprod()

    return result

不同j净值及准确率如下

j = 10时,预测准确率61.4%,策略净值2.38

整体来看,策略效果并不好,练手, 看看就好,欢迎指正!!!

sh

0 人点赞