回归作为数据分析中非常重要的一种方法,在量化中的应用也很多,从最简单的因子中性化到估计因子收益率,以及整个Barra框架,都是以回归为基础,本文总结各种回归方法以及python实现的代码。
OLS
回归是研究多组自变量X1,X2,...,Xn与一个因变量Y关系的模型,首先从最简单的OLS开始,变量假设如下
回归模型可以表示为
同时线性回归还必须满足“BLUE”的假设,在这些假设下,回归的目标是在已知X,Y的情况下估计回归系数beta,OLS的思想是最小化残差平方和,即
OLS估计量具有一致性、无偏性等优点。
接下用用python实现OLS,所用数据为特定日期全A股的PB、ROE、行业、市值数据,部分数据如下,数据和代码获取后台回复“回归”。
python中实现OLS的模块很多,numpy、sklearn、statsmodels中都有,这里给出numpy,statsmodel中的用法。
lstsq的输入包括三个参数,a为自变量X,b为因变量Y,rcond用来处理回归中的异常值,一般不用。
lstsq的输出包括四部分:回归系数、残差平方和、自变量X的秩、X的奇异值。一般只需要回归系数就可以了。
这里需要注意的一点是,必须自己在自变量中添加截距项,否则回归结果是没有截距项的,其他细节可以参考help。
这里我们用一个行业股票的数据实现PB-ROE回归,代码如下
datas = pd.read_excel('data.xlsx',index_col = 0)
datas = datas.loc[(datas.pb_lf>0) &(datas.pb_lf<10) ]
datas = datas.loc[(datas.roe_ttm2>0) &(datas.roe_ttm2 < 10)]
datas1 = datas.loc[datas.classname == '医药生物']
x = datas1[['roe_ttm2']]
x['Intercept'] = 1
y = datas1['pb_lf']
beta,resid,ranks,s = np.linalg.lstsq(x,y)
y_fitted = beta[0]*datas1[['roe_ttm2']] + beta[1]
plt.figure(figsize = (8,6))
plt.plot(datas1.roe_ttm2, datas1.pb_lf,'ko', label='sample')
plt.plot(datas1.roe_ttm2, y_fitted, 'black',label='OLS',linewidth = 2)
plt.xlabel('ROE')
plt.ylabel('PB')
plt.legend()
plt.show()
关于PB-ROE
PB-ROE提供了一种投资的框架,这种框架是说,股票的PB和ROE之间存在近似的线性关系,ROE越高,PB越高,因此如果同时根据PB、ROE值来投资,很难选到同时满足PB最小、ROE最大的股票。但可以根据他们的线性关系进行选择,回归直线上的点可以视为合理的PB、ROE组合水平,这样位于回归线下方的股票都是PB被低估的,未来有很大的上升修复空间,而位于回归线上方的股票都是当前PB被高估的,未来会下降,因此投资可以选择位于回归线下方的股票。使用这种方法最重要的点是回归必须是靠谱的,比如ROE应该是稳定的,确保未来可持续,比如应想办法消除行业间的差异等等。
lstsq比较方便用在只需要回归系数的情况下,如果需要对回归结果做评估,比如算拟合值、算残差、算R2,做t检验、F检验、算P值,就很麻烦了,而statsmodel恰好适合这种情况。
statsmodels中做回归有很多模块都能实现,sml.ols的优点是可以写成公式型的回归,类似R中做回归的过程,比如PB和ROE的回归可以用公式表示为'pb~roe',多个自变量之间用加号连接。sml.ols一般包括formula和data两个输入,formula是回归的公式,data为使用的数据。此外,还有missing这个参数,对于回归数据包含缺失值时很好用,比如设置missing = 'drop'表示回归时删除包含缺失值的样本。代码如下
import statsmodels.formula.api as sml
model = sml.ols(formula='pb_lf~roe_ttm2',data = datas1)
result=model.fit()
result.params # 回归系数
result.rsquared # R方
result.resid # 残差
result.fittedvalues # 拟合值
用summary函数可以出比较美观的结果。
sm.ols是statsmodels中另一个回归的模块,它的输入类似lstsq,输入变量y,x即可,这里使用patsy中的dmatrics生成x,y,需要注意的是,dmatrices生生成的x是自带截距项的,代码如下,summary输出结果同上。
import statsmodels.api as sm
from patsy import dmatrices
from scipy.linalg import toeplitz
import numpy.linalg as la
y,X=dmatrices('pb_lf~roe_ttm2',data=datas1,return_type='dataframe')
mod = sm.OLS(y,X)
res = mod.fit()
res.summary()
GLS
GLS是广义最小二乘法的缩写,刚才总结的OLS满足很多假设,但实际数据往往没有那么好的性质,GLS用来解决异方差的问题,在数据有异方差的问题时,OLS的结果不再具有无偏性等性质,GLS的结果更好。它的主要思想是给解释变量加上一个权重,从而使得加上权重后的回归方程方差是相同的.因此在GLS方法下可以得到估计量的无偏和一致估计。
使用这种方法的前提时,你已经对误差项的协方差阵有了较好的估计。statsmodel中实现GLS的模块如下
常用的输入包括因变量endog,自变量exog,残差的协方差阵sigma,missing设定样本中缺失值的处理方法,这里exog也是不带截距项的,需要自己加入,可以用sm.add_constant(),代码如下
x = datas1[['roe_ttm2','mktcap']]
x['mktcap'] = np.log(x.mktcap)
sigma = x.T.var()
x = sm.add_constant(x)
y = datas1['pb_lf']
sm.GLS(y, x,sigma = sigma).fit().params
WLS
WLS是加权最小二乘法的简称,如果仔细看上一张图GLS函数的说明,可以看到,当sigma是一个向量的时候,GLS等价于WLS,即WLS表示残差的协方差阵是对角阵。可以用sm.WLS或者sm.GLS实现,代码同上。
RLS
RLS表示带约束的最小二乘法,这里的约束只包括线性约束,可以表示为AX = B的形式,如果有其他类型的约束,需要用其他方法,数学上可以证明,线性约束下,最小二乘法仍有最优解。rls的实现可以使用statsmodels.sandbox.rls。函数说明如下
endog表示Y,exog表示X,constr线性约束的A,params表示线性约束的B,默认为0,sigma是权重,同GLS。大部分跟之前都是一样的,唯一需要注意的是约束的输入,根据约束条件写出A,B然后输入。
带约束的最小二乘法在量化中非常常用,比如做行业中性化时,如果所有行业虚拟变量都保留,并且添加了截距项的情况下,会出现变量多重共线性,回归结果无效,这时候一种方法是删除一个虚拟变量,还有一种方法是添加一个约束。比如可以添加行业的市值占比和系数乘积的和为0:
其中,w为各行业流通市值占比,这种方法下,对pb因子做中性化的代码如下
from statsmodels.sandbox.rls import *
weights = datas.mktcap.groupby(datas.classname).sum()/datas.mktcap.sum()
class_var = pd.get_dummies(datas['classname'],columns=['classname'],prefix='class',
prefix_sep="_", dummy_na=False, drop_first = False)
x = class_var
x = pd.concat([class_var,np.log(datas.mktcap)],axis = 1)
x = sm.add_constant(x)
y = datas['pb_lf']
con1= [0] + list(weights) + [0]
models = RLS(y, x,constr = con1).fit()
models.resid
Logistic
Logistic回归是一种用来做Y是类别变量的方法,可以用statsmodels.discrete.discrete_model.Logit实现,代码输入跟之前的差不多
写在最后
本文总结了比较常用的一些方法,除此外,还有Lasso、Ridge等回归方法,可以用sklearn实现,不再赘述,列出一些参考网站,如果有没有写清楚的地方,可以再看一看。