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

量化交易吧 /  数理科学 帖子:3364764 新帖:2

【配对交易】基于zscore的配对交易策略(胜率88.

我是一个土豆发表于:5 月 10 日 03:19回复(1)

7个月前我发布了【均值回归】基于zscore的均值回归策略(胜率100%)一文,
我将zscore思想用于单支股票,思想是股价和其均线值的差价在一定范围内波动,并计算其zscore值,获取买卖信号。

在本策略中,我将zscore思想还本复原,用于配对交易,利用两支相关性强的股票价格差价在一定范围内波动的思想,进行交易。具体做法如下:

  • 1、计算沪深300指数成分股近10年股价相关系数,根据相关系数进行配对股票选择

  • 2、设定zscore上下限(即买卖信号点),zscore窗口大小

  • 3、在每个交易日,计算zscore值

  • 4、若触发上下限,则卖出超涨股,买入超跌股

经过对比调试,本策略最终选择了海康威视格力电器两支股票进行配对交易,回测结果如下,从12年初到19年3月,大概7年多的时间,年化45.38%,夏普1.169,胜率88.2%(一共交易17次),最大回撤53.75%


本策略思想很简单,编写得也较为粗糙,仓位一直保持满仓状态,无止损,导致15年股灾期间出现较大回撤。

另外,在选股方面,相比于之前发布的单股票策略选股的主观性,本策略利用相关系数进行选股,相对更客观,但对于指数的选择(此处选择沪深300指数)可自行调整。

共勉

1、相关性分析¶

# 获取指数成分股历史股价index = '000300.XSHG'start = '2009-1-1'end   = '2019-1-1'code_list = get_index_stocks(index)index_df = get_price(code_list, start, end, fields='close').closeindex_df.head()
/opt/conda/lib/python3.6/site-packages/jqresearch/api.py:86: FutureWarning: 
Panel is deprecated and will be removed in a future version.
The recommended way to represent these types of 3-dimensional data are with a MultiIndex on a DataFrame, via the Panel.to_frame() method
Alternatively, you can use the xarray package http://xarray.pydata.org/en/stable/.
Pandas provides a `.to_xarray()` method to help automate this conversion.

  pre_factor_ref_date=_get_today())

.dataframe tbody tr th:only-of-type {        vertical-align: middle;    }    .dataframe tbody tr th {        vertical-align: top;    }    .dataframe thead th {        text-align: right;    }


000001.XSHE000002.XSHE000063.XSHE000069.XSHE000100.XSHE000157.XSHE000166.XSHE000333.XSHE000338.XSHE000402.XSHE000408.XSHE000413.XSHE000415.XSHE000423.XSHE000425.XSHE000503.XSHE000538.XSHE000553.XSHE000568.XSHE000625.XSHE000627.XSHE000630.XSHE000651.XSHE000661.XSHE000671.XSHE000703.XSHE000709.XSHE000725.XSHE000728.XSHE000768.XSHE000776.XSHE000783.XSHE000786.XSHE000792.XSHE000826.XSHE000839.XSHE000858.XSHE000876.XSHE000895.XSHE000898.XSHE...601633.XSHG601668.XSHG601669.XSHG601688.XSHG601727.XSHG601766.XSHG601788.XSHG601800.XSHG601808.XSHG601818.XSHG601828.XSHG601838.XSHG601857.XSHG601877.XSHG601878.XSHG601881.XSHG601888.XSHG601898.XSHG601899.XSHG601901.XSHG601919.XSHG601933.XSHG601939.XSHG601985.XSHG601988.XSHG601989.XSHG601991.XSHG601992.XSHG601997.XSHG601998.XSHG603156.XSHG603160.XSHG603259.XSHG603260.XSHG603288.XSHG603799.XSHG603833.XSHG603858.XSHG603986.XSHG603993.XSHG
2009-01-053.235.189.223.131.192.60NaNNaN1.644.804.021.391.6112.132.473.6716.365.3913.161.671.671.912.927.980.370.933.372.276.445.764.233.102.0331.913.682.3011.362.527.686.71...NaNNaNNaNNaN5.383.80NaNNaN11.31NaNNaNNaN8.11NaNNaNNaNNaN5.962.69NaN7.60NaN2.36NaN1.79NaN5.63NaNNaN2.82NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2009-01-063.435.349.593.231.212.86NaNNaN1.764.944.161.421.7112.402.723.8216.015.5613.401.671.751.992.848.050.380.983.702.346.606.144.233.182.0928.734.022.3811.822.617.457.05...NaNNaNNaNNaN5.923.82NaNNaN11.82NaNNaNNaN8.28NaNNaNNaNNaN6.332.79NaN8.01NaN2.41NaN1.82NaN5.70NaNNaN2.88NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2009-01-073.325.319.203.321.202.83NaNNaN1.754.884.221.451.7413.002.783.8216.125.5313.251.671.872.052.818.290.380.983.632.336.526.254.233.142.0925.853.942.3611.682.687.346.95...NaNNaNNaNNaN5.863.70NaNNaN11.79NaNNaNNaN8.20NaNNaNNaNNaN6.292.72NaN8.13NaN2.35NaN1.78NaN5.77NaNNaN2.83NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2009-01-083.195.348.883.361.152.74NaNNaN1.714.864.191.471.7613.182.743.6715.825.3213.091.671.791.952.798.460.390.963.472.236.366.424.233.091.9823.623.962.2511.532.597.336.67...NaNNaNNaNNaN5.543.58NaNNaN11.54NaNNaNNaN7.95NaNNaNNaNNaN6.152.61NaN7.74NaN2.26NaN1.75NaN5.50NaNNaN2.74NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2009-01-093.285.338.963.291.182.85NaNNaN1.764.904.211.471.7613.192.793.7615.615.4313.281.671.832.032.859.260.391.013.542.276.526.454.233.152.0524.164.132.4811.792.677.556.77...NaNNaNNaNNaN5.693.64NaNNaN12.04NaNNaNNaN7.98NaNNaNNaNNaN6.352.65NaN7.91NaN2.28NaN1.77NaN5.94NaNNaN2.77NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
import pandas as pd# 计算每支股票与其余股票的相关系数,降序排列# 相关系数相等的两支股票即为配对股票corr_df = index_df.corr()corr_df[corr_df==1] = nancorr_df = pd.DataFrame(corr_df.max().sort_values(ascending=False).head(20), columns=['corr'])corr_df['name'] = [get_security_info(code).display_name for code in corr_df.index]corr_df

.dataframe tbody tr th:only-of-type {        vertical-align: middle;    }    .dataframe tbody tr th {        vertical-align: top;    }    .dataframe thead th {        text-align: right;    }


corrname
601398.XSHG0.992742工商银行
601939.XSHG0.992742建设银行
000858.XSHE0.988095五粮液
000333.XSHE0.988095美的集团
002415.XSHE0.986354海康威视
000651.XSHE0.986354格力电器
002508.XSHE0.984371老板电器
002572.XSHE0.984371索菲亚
601288.XSHG0.984116农业银行
600036.XSHG0.982082招商银行
601318.XSHG0.982082中国平安
600893.XSHG0.981993航发动力
600118.XSHG0.981993中国卫星
600104.XSHG0.981922上汽集团
600660.XSHG0.981922福耀玻璃
601390.XSHG0.981689中国中铁
601186.XSHG0.981689中国铁建
000568.XSHE0.981355泸州老窖
002142.XSHE0.981053宁波银行
600741.XSHG0.977252华域汽车

2、计算zscore¶

window = 60  # zscore窗口code1 = '002415.XSHE'code2 = '000651.XSHE'name1 = corr_df.loc[code1, 'name']name2 = corr_df.loc[code2, 'name']price_df = get_price([code1, code2], end_date=end, count=len(index_df)+window-1, fields='close').closeprice_df.columns = [name1, name2]price_df.dropna(inplace=True)sub = price_df[name1] - price_df[name2]ma = sub.rolling(window).mean()std = sub.rolling(window).std()price_df['zscore'] = round((sub - ma) / std, 4)price_df.dropna(inplace=True)price_df.head()

.dataframe tbody tr th:only-of-type {        vertical-align: middle;    }    .dataframe tbody tr th {        vertical-align: top;    }    .dataframe thead th {        text-align: right;    }


海康威视格力电器zscore
2010-08-243.855.66-1.2659
2010-08-253.815.52-0.9804
2010-08-263.905.54-0.7757
2010-08-273.915.54-0.7262
2010-08-304.035.65-0.6745
sample = price_df.head(800)# 正常显示负号plt.rcParams['axes.unicode_minus'] = Falseplt.figure(figsize=(20, 10))plt.plot(sample[name1], label=name1)plt.plot(sample[name2], label=name2)plt.legend(loc=1)plt.twinx()plt.plot(sample['zscore'], label='zscore', c='g')plt.axhline(2, c='r'); plt.axhline(-2, c='r')plt.legend(loc=2)plt.show()

全部回复

0/140

量化课程

    移动端课程