请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3365876 新帖:9

读《基于协整的搬砖策略》有感,有趣的协整!

汇市江湖百晓生发表于:5 月 18 日 19:18回复(1)

本人量化入门小白一枚,拜读了大神发的 【量化课堂】基于协整的搬砖策略 这一帖子,发现协整是一个可以作为入门的策略,于是查阅了若干英文文献,基于前辈的想法,重新做了一遍,做了一点点修改(主要是对于协整检验的完善),策略还很不完善,欢迎各位大神批评指正。

基于协整关系的配对交易策略

配对交易是统计套利的一种典型手段,通过选择一对具有相似的价格走势的股票,并通过对两只股票的多空设置,实现期望的profit。传统的配对交易是基于两只股票间的相关性以及其他的非参数决策规则。相关性主要用于描述短期的关系,在描述长期性的关系时有所欠缺。因此这里我们参考诸多关于配对交易的文献,使用协整来描述两只股票之间长期的关系。

在说到协整的概念之前,先要说一下时间序列的稳定性,对于一个时间序列{Xt},满足以下条件,就可以称之为弱平稳,记为I(0): 1、E(Xt)=μ,与时间不相关 2、V(Xt)=S**2,与时间不相关 3、协方差只与时间间隔p相关

一个时间序列{Yt}在称为稳定序列之前,要经过d次差分,则称{Yt}为d阶单整,记为I(d)。 假如两个非稳定时间序列{Xt}{Yt}都为1阶单整,且存在一个b,Yt=bXt+C C为常数, 使得{Yt-bXt}为I(0),则称{Xt}{Yt}协整。

本次研究中稳定性检验使用ADF方法,协整检验使用EG两步法。投资组合设置为市场中性,研究思路如下: 1、首先使用Python Statsmodels扩展包中的内置函数coint()来初步选择两只可能存在协整关系的股票,选择的股票区间采用 【量化课堂】基于协整的搬砖策略 这一研究中的12支股票。筛选的标准为根据返回的p值( MacKinnon's approximate p-value based on MacKinnon)。p值越小,代表协整关系越强。 2、画出两只股票的log price 走势图,直观的感受一下相关性。 3、对两只股票的log price组成的时间序列做ADF检验(平稳性检验),若不能拒绝原假设,则继续差分,再次进行ADF检验。 4、一阶差分之后发现可以拒绝原假设,则两个时间序列是一阶单整,可以进行协整检验。 5、采用OLS方法确定协整系数,并对拟合的残差进行单整检验(ADF方法) 6、基于回归系数构建平稳序列,Zt=Yt-b*Xt 7、定义zscore=(Zt-Zt.mean())/(Zt.std),zscore代表偏离平均值几个sigma。 8、依据zscore构建交易信号。

1、首先使用Python Statsmodels扩展包中的内置函数coint()来初步选择两只可能存在协整关系的股票,选择的股票区间采用 社区里面现有的帖子 : 【量化课堂】基于协整的搬砖策略
这一研究中的12支股票。筛选的标准为根据返回的p值( MacKinnon's approximate p-value based on MacKinnon)。p值越小,代表协整关系越强。

import numpy as np
import pandas as pd
import statsmodels.api as sm
import seaborn as sns

def find_cointergrated_pairs(dataframe):
    n=dataframe.shape[1]
    pvalue_matrix=np.ones((n,n))
    keys=dataframe.keys()
    pairs=[]
    for i in range(n):
        for j in range(i+1,n):
            stk1=dataframe[keys[i]]
            stk2=dataframe[keys[j]]
            result=sm.tsa.stattools.coint(stk1,stk2)
            pvalue=result[1]
            pvalue_matrix[i, j] = pvalue
            if pvalue<0.05:
                pairs.append((keys[i],keys[j],pvalue))
    return pvalue_matrix,pairs
            
    
stock_list = ["002142.XSHE", "600000.XSHG", "600015.XSHG", "600016.XSHG", "600036.XSHG", "601009.XSHG",
              "601166.XSHG", "601169.XSHG", "601328.XSHG", "601398.XSHG", "601988.XSHG", "601998.XSHG"]
prices_df = get_price(stock_list, start_date="2014-01-01", end_date="2015-01-01", frequency="daily", fields=["close"])["close"]
log_price_df=np.log(prices_df)
pvalues, pairs = find_cointergrated_pairs(log_price_df)
sns.heatmap(1-pvalues, xticklabels=stock_list, yticklabels=stock_list, cmap='RdYlGn_r',mask = (pvalues == 1))
print pairs
[('600000.XSHG', '600015.XSHG', 0.03983508244814473), ('600000.XSHG', '601988.XSHG', 0.022753486859363257), ('601398.XSHG', '601988.XSHG', 0.0154252055760712)]

2、画出两只股票的log price 走势图,直观的感受一下相关性。

import matplotlib.pyplot as plt
stk1_price=log_price_df['600015.XSHG']
stk2_price=log_price_df['600000.XSHG']
plot(stk1_price)
plot(stk2_price)
plt.xlabel('Time')
plt.ylabel('ClosePrice')
plt.legend(['600015.XSHG','600000.XSHG'],loc='upper center')
<matplotlib.legend.Legend at 0x7ff502c60690>

3、对两只股票的log price组成的时间序列做ADF检验(平稳性检验),若不能拒绝原假设,则继续差分,再次进行ADF检验。

from statsmodels.tsa.stattools import adfuller
def testStationarity(data):
    adftest=adfuller(data)
    result=pd.Series(adftest[0:4],index=['adf','pvalue','lags used','number of pbservations'])
    for key,value in adftest[4].items():
        result['Critical value(%s)'%key]=value
    return result
x=np.array(stk1_price)
y=np.array(stk2_price)
zz=pd.concat([testStationarity(x),testStationarity(y)],axis=1)
zz.columns=['600015','600000']
zz
600015 600000
adf 2.263274 2.359863
pvalue 0.998931 0.998990
lags used 12.000000 12.000000
number of pbservations 232.000000 232.000000
Critical value(5%) -2.874080 -2.874080
Critical value(1%) -3.458855 -3.458855
Critical value(10%) -2.573453 -2.573453

首先对两只股票收盘价组成的时间序列进行单整检验,第一步进行平稳性检验,若不能拒绝原假设,进行一次差分,再进行平稳性检验,看两者是否是同阶单整。ADF检验方法。

进行一次差分,然后再次检验平稳性。

diffx=stk1_price.diff(1)
diffx.dropna(inplace=True)
diffx=np.array(diffx)
diffy=stk2_price.diff(1)
diffy.dropna(inplace=True)
diffy=np.array(diffy)
tz=pd.concat([testStationarity(diffx),testStationarity(diffy)],axis=1)
tz.columns=['600015','600000']
tz
600015 600000
adf -7.614129e+00 -3.955579
pvalue 2.214907e-11 0.001662
lags used 4.000000e+00 11.000000
number of pbservations 2.390000e+02 232.000000
Critical value(5%) -2.873710e+00 -2.874080
Critical value(1%) -3.458011e+00 -3.458855
Critical value(10%) -2.573256e+00 -2.573453

一阶差分以后,两个序列就已经平稳了,他们的单整阶数都是一,所以是单整同阶的,下面就可以做协整了:这里的原假设是两者不存在协整关系。 协整检验如下。

from statsmodels.tsa.stattools import coint
coint_result=coint(x,y)
pvalue=coint_result[1]
print 'pvalue=',pvalue
pvalue= 0.051264531578

5、采用OLS方法确定协整系数,并对拟合的残差进行单整检验(ADF方法)

pvalue在0.05附近,可以认为协整关系非常强。接下来,我们用这两支股票的价格来进行一次OLS线性回归,以此算出它们是以什么线性组合的系数构成平稳序列的。

x = stk1_price
y = stk2_price
X = sm.add_constant(x) 
result = (sm.OLS(y,X)).fit()
print(result.summary())
a=result.resid
residus=[]
for k,v in a.iteritems():
    residus.append((v))
b=np.array(residus)
bz=pd.concat([testStationarity(x),testStationarity(y),testStationarity(b)],axis=1)
bz.columns=['600015','600000','residus']
bz
                            OLS Regression Results                            
==============================================================================
Dep. Variable:            600000.XSHG   R-squared:                       0.953
Model:                            OLS   Adj. R-squared:                  0.953
Method:                 Least Squares   F-statistic:                     4954.
Date:                Fri, 17 Feb 2017   Prob (F-statistic):          1.27e-163
Time:                        20:07:31   Log-Likelihood:                 514.80
No. Observations:                 245   AIC:                            -1026.
Df Residuals:                     243   BIC:                            -1019.
Df Model:                           1                                         
Covariance Type:            nonrobust                                         
===============================================================================
                  coef    std err          t      P>|t|      [95.0% Conf. Int.]
-------------------------------------------------------------------------------
const           0.0115      0.030      0.390      0.697        -0.047     0.070
600015.XSHG     1.1056      0.016     70.386      0.000         1.075     1.137
==============================================================================
Omnibus:                       19.805   Durbin-Watson:                   0.183
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               28.977
Skew:                           0.526   Prob(JB):                     5.10e-07
Kurtosis:                       4.316   Cond. No.                         37.5
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
600015 600000 residus
adf 2.263274 2.359863 -3.423230
pvalue 0.998931 0.998990 0.010192
lags used 12.000000 12.000000 0.000000
number of pbservations 232.000000 232.000000 244.000000
Critical value(5%) -2.874080 -2.874080 -2.873459
Critical value(1%) -3.458855 -3.458855 -3.457438
Critical value(10%) -2.573453 -2.573453 -2.573122

系数是1.1056.对残差进行平稳性检验,可以拒绝原假设。接下来画出拟合曲线和数据

fig, ax = plt.subplots(figsize=(12,7))
ax.plot(x, y, 'o', label="data")
ax.plot(x, result.fittedvalues, 'r', label="OLS")
ax.legend(loc='best')
<matplotlib.legend.Legend at 0x7ff50979a8d0>

6、基于回归系数构建平稳序列,Zt=Yt-b*Xt

设浦发银行的股价为Y,华夏银行的股价为X,则回归的拟合结果为:Y=1.1056X+0.0115,也就是说Y-1.1056X是平稳序列。 依照这个比例,我们画出它们价差的平稳序列。可以看出,虽然价差上下波动,但都会回归中间的均值。

plot(1.1056*stk1_price-stk2_price)
plt.axhline((1.1056*stk1_price-stk2_price).mean(), color="red", linestyle="--")
plt.xlabel("Time"); plt.ylabel("Stationary Series")
plt.legend(["Stationary Series", "Mean"])
<matplotlib.legend.Legend at 0x7fbe285b1a90>

7、定义zscore=(Zt-Zt.mean())/(Zt.std),zscore代表偏离平均值几个sigma。 8、依据zscore构建交易信号。

def zscore(series):
    return (series - series.mean()) / np.std(series)
plot(zscore(1.1056*stk1_price-stk2_price))
plt.axhline(zscore(1.1056*stk1_price-stk2_price).mean(), color="black")
plt.axhline(1.0, color="red", linestyle="--")
plt.axhline(-1.0, color="green", linestyle="--")
plt.legend(["z-score", "mean", "+1", "-1"])
<matplotlib.legend.Legend at 0x7ff502b63350>

当两这个序列的 z-score 突破 1 或者 −1 时,说明两支股票的价差脱离了统计概念中的合理区间,如果它们的协整关系能够保持,那么它们的价差应该收敛。所以,在发现上述序列突破 1或 −1 时,应该按照比例买多一支股票并做空另外一支,从而赚取之后收敛的差价。

结合上图,当 z-score 突破上方红线时,说明浦发银行的价格相对于华夏银行高估,因此我们买入 1 份华夏银行并卖空 1.3689 份浦发银行(系数根据前面的线性回归得出),并当 z-score 回归于 0 时清仓获利。如果 z-score 突破下方绿线的话,反方向操作。

由于A股市场不可以做空,因此将所有的做空操作改为全部卖出。

全部回复

0/140

达人推荐

量化课程

    移动端课程