normed_spread.plot()
plt.axhline(normed_price(spread_mean))
plt.axhline(1,color='green',linestyle='--')
plt.axhline(-1,color='red',linestyle='--')
plt.legend(labels=['spread','mean','+1','-1'],loc='best')
五、滚动窗口的z分数变化
我们以30天的滚动窗口的均值和标准差来求每天的z分数。
avg_1=spread.rolling(1).mean()
avg_30=spread.rolling(30).mean()
std_30=spread.rolling(30).std()
normed_spread=(avg_1-avg_30)/std_30
normed_spread.plot(label='Rolling 30 day Z score')
plt.axhline(0,color='black')
plt.axhline(1,color='red',ls='--')
plt.legend()
归一化的z score
六、Quantopian上面的策略实施
流程
我们用代码来实现
import numpy as np
#在算法开始前都要先初始化,将相关内容定义
def initialize(context):
schedule_function(check_pairs, date_rules.every_day(), time_rules.market_close(minutes=60))#每天都定时检查一下股价差价z score的变化情况并作出安排
context.aal = sid(45971) #aal
context.ual = sid(28051) #ual
context.short_top=False#我们在这里标注一下是否进行了交易,以差价里面的被减数的那只股票作为标志
context.long_top=False
def check_pairs(context, data):
aal=context.aal
ual=context.ual
prices=data.history([aal, ual], 'price',30,'1d')#data.history传入包括股票,字段,周期,频率等字段,在这里返回的是一个dataframe
mavg_30=np.mean(prices[ual]-prices[aal])
mstd_30=np.std(prices[ual]-prices[aal])
short_price=prices.iloc[-1:]
mavg_1=np.mean(short_price[ual]-short_price[aal])
#判断z score的相对大小
if mstd_30>0:
z_score=(mavg_1-mavg_30)/mstd_30
if z_score>0.5 and not context.short_top:
order_target_percent(ual, -0.5)#卖出高价的
order_target_percent(aal, 0.5)#买入低价的
context.short_top=True
context.long_top=False
elif z_score<-0.5 and not context.long_top:
order_target_percent(ual, 0.5)#买入被减数的股票
order_target_percent(aal, -0.5)#卖出减数的股票
context.short_top=False
context.long_top=True
if abs(z_score)<0.1:#如果z score变化很小,那么就按兵不动
order_target_percent(ual, 0)#买入被减数的股票
order_target_percent(aal, 0)#卖出减数的股票
context.short_top=False
context.long_top=False
record('z score', z_score)
变动日志
可以看到实现的算法能够根据z score的波动来做出反应
参考资源
配对交易 - MBA智库百科
搬砖的理论基础:配对交易 Pair Trading - 雪球