|
基于时间序列的协整关系的配对交易
本帖主要介绍了协整的基础知识,如何对两个时间序列进行协整关系检验,并实现了一个简单的配对交易策略。
这里参考了百度百科和链接。
Why
统计套利之配对交易 是一种基于数学分析交易策略,其盈利模式是通过两个标的的差价(spread)来获取,两者的价格走势虽然在中途会有所偏离,但是最终都会趋于一致。
配对交易就是利用这种价格偏离获取收益。具有这种关系的两个标的,在统计上称作 协整性(cointegration),即它们之间的差价会围绕某一个均值来回摆动,这是配对交易策略可以盈利的基础。
通俗点来讲,如果两个股票或者变量之间具有强协整性,那么不论它们中途怎么走的,它们的目的地总是一样的。
What
经典回归模型是建立在平稳数据变量的基础之上的,对于非平稳变量,不能使用经典回归模型,否则会出现虚假回归等诸多问题,但是实际应用中大多数时间序列是非平稳的。
1987年Engle和Granger提出的协整理论及其方法,为非平稳序列的建模提供了另一种途径。
虽然一些经济变量的本身是非平稳序列,但是,它们的线性组合却有可能是平稳序列。这种平稳的线性组合被称为协整方程,且可解释为变量之间的长期稳定的均衡关系。
需要 特别注意 的是 协整性 和 相关性 虽然比较像,但实际是不同的两个东西。
两个变量之间可以相关性强,协整性却很弱,比如说两条直线,y=x和y=2x,它们之间的相关性是1,但是协整性却比较差;
方波信号和白噪声信号,它们之间相关性很弱,但是却有强协整性。(如下图)
my picture
How:统计套利之配对交易
这里还有介绍一个关于时间序列的基本概念:平稳性。
平稳分为两种:
强(严)平稳:给定随机过程X(t),t属于T,其有限维分布组为F(x1,x2,…xn;t1,t2,…,tn),t1,t2,…,tn属于T,对任意n任意的t1,t2,…,tn属于T,任意满足t1+h,t2+h,…,tn+h属于T的h,总有F(x1,x2,…xn;t1,t2,…,tn)=F(x1,x2,…xn;t1+h,t2+h,…,tn+h);
宽(弱)平稳:给定二阶矩过程(二阶矩存在)X(t),t属于T,如果X(t)的均值函数u(t)是常数,相关函数R(t1,t2)=f(t2-t1)即相关函数只与时间间隔有关。(我们通常用的都是弱平稳)
单整阶数:当原序列是非平稳的,需要对序列进行差分(后一项减前一项),直到为平稳序列,差分几次就是几阶。
协整关系存在的条件是:只有当两个变量的时间序列{x}和{y}是同阶单整序列即I(d)时,才可能存在协整关系(这一点对多变量协整并不适用)。因此在进行y和x两个变量协整关系检验之前,先用ADF单位根检验对两时间序列{x}和{y}进行平稳性检验。平稳性的常用检验方法是图示法与单位根检验法。
下面举个例子,为了证明两个symbol是否存在协整性:取一年的时间序列的 binance的BTC/USDT 和 binance的ETH/USDT
隐藏输出
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from vitu import api
data1 = api.coinbar(symbol='BTCUSDT',exchange='binance',freq='daily',start_date='2019-01-01',end_date='2019-12-01')['close']
data1 = data1.reset_index(drop=True)
data1.columns = ['binance.btcusdt']
data1.plot(figsize=(20,8))
plt.show()
隐藏输出
data2 = api.coinbar(symbol='ETHUSDT',exchange='binance',freq='daily',start_date='2019-01-01',end_date='2019-12-01')['close']
data2 = data2.reset_index(drop=True)
data2.columns = ['binance.ethusdt']
data2.plot(figsize=(20,8))
plt.show()
先定单整阶数,即检验平稳性,作差分,直到序列平稳。
这里检验平稳性用的是 ADF单位根检验法 ,原假设为序列具有单位根,即非平稳,对于一个平稳的时序数据,就需要在给定的置信水平上显著,拒绝原假设。
隐藏输出
from statsmodels.tsa.stattools import adfuller,coint
def testStationarity(data):
adftest = adfuller(data)
result = pd.Series(adftest[0:4], index=['Test Statistic','p-value','Lags Used','Number of Observations Used'])
for key,value in list(adftest)[4].items():
result['Critical Value (%s)'%key] = value
return result
x=np.array(data1)
y=np.array(data2)
zz=pd.concat([testStationarity(x),testStationarity(y)],axis=1)
zz.columns=['binance.btcusdt','binance.ethusdt']
diffx=data1.diff(1)
diffx.dropna(inplace=True)
diffx=np.array(diffx)
diffy=data2.diff(1)
diffy.dropna(inplace=True)
diffy=np.array(diffy)
tz=pd.concat([testStationarity(diffx),testStationarity(diffy)],axis=1)
zz.columns=['binance.btcusdt','binance.ethusdt']
zz
binance.btcusdt binance.ethusdt
Test Statistic -0.704307 -1.474204
p-value 0.845631 0.546231
Lags Used 0.000000 15.000000
Number of Observations Used 333.000000 318.000000
Critical Value (1%) -3.450141 -3.451082
Critical Value (5%) -2.870259 -2.870672
Critical Value (10%) -2.571415 -2.571636
一阶差分以后,两个序列就已经平稳了,他们的单整阶数都是一,所以是单整同阶的,下面就可以做协整了:这里的原假设是两者不存在协整关系。
隐藏输出
a,pvalue,b = coint(x,y)
pvalue
1.5536553497038895e-17
p值低于临界值,所以拒绝原假设,两者存在协整关系。
接下来就可以根据两者的协整关系做配对交易了。先画出两者的差价序列:
隐藏输出
mean=(data1-data2).mean()
std=(data1-data2).std()
s1=pd.Series(mean,index=range(len(data1)))
s2=pd.Series(mean+std,index=range(len(data1)))
s3=pd.Series(mean-std,index=range(len(data1)))
data3=pd.concat([data1-data2,s1,s2,s3],axis=1)
data3.columns=['spreadprice','mean','upper','down']
data3.plot(figsize=(14,7))
print(mean+std,mean-std)
plt.grid()
plt.show()
9920.702739095435 4481.417979467437
这样,一个最简单的配对交易策略如下:(如果可以操作期货的话)
spreadprice大于 9920.7027 时,卖空差价,即卖空 BTC/USDT,买入 ETH/USDT。
spreadprice小于 4481.4179 时,买入差价,即买入 BTC/USDT,卖空 ETH/USDT。
spreadprice靠近零时,平仓
上述过程重在描述理论,以供大家研究,结果不是太重要。(下面策略附源代码)
期货回测即将开启,或许这种统计套利的方式在期货市场也有用武之地。
量化之路漫漫其修远兮,吾将上下而求索。
隐藏输出
# 引入需要的python库
from vitu import ai
import numpy as np
# 配置一个账户初始持仓信息
ai.create_account(name='myaccount', exchange='binance', account_type='digital.spot', position_base=[{'asset': 'USDT', 'qty': 200000}])
# 选定投资域
universe = ai.create_universe(['BTC/USDT.binance','ETH/USDT.binance'])
# 初始化环境
def initialize(context):
context.universe = ['BTC/USDT.binance','ETH/USDT.binance']
context.account = context.get_account('myaccount')
context.amount = 0
# 策略逻辑
def handle_data(context):
symbol1 = context.universe[0]
symbol2 = context.universe[1]
price1 = context.get_price(symbol1)
price2 = context.get_price(symbol2)
buy_list = []
sell_list = []
if price1 - price2 > 9912.94:
buy_list.append(symbol2)
sell_list.append(symbol1)
if price1 - price2 < 4468.02:
buy_list.append(symbol1)
sell_list.append(symbol2)
if price1-price2 < 0.01 and price1-price2 > -0.01 :
sell_list.append(symbol1)
sell_list.append(symbol2)
hold = []
buy = []
for symbol in context.universe:
if symbol in sell_list:
price = context.get_price(symbol1)
context.account.sell(symbol, price, context.amount*0.1)
else:
hold.append(symbol)
buy = hold
for symbol in buy_list:
if symbol not in hold:
buy.append(symbol)
if len(buy) > 0:
available = context.account.get_position('USDT')['available']
price = context.get_price(symbol)
context.amount = available/price
amout = available/len(buy)
for symbol in buy:
context.account.buy(symbol, price, context.amount*0.1)
# 配置策略参数
my_strategy = ai.create_strategy(
initialize,
handle_data,
universe=universe,
benchmark='csi5',
freq='d',
refresh_rate=1
)
# 配置回测参数
ai.backtest(strategy=my_strategy,
start='2019-01-10',
end='2019-12-01',
commission={'taker': 0.0002, 'maker': 0.0002}
)
累计收益率
94.81%
基准累计收益率
61.15%
年化收益率
110.99%
基准年化收益率
70.62%
阿尔法
0.51
夏普比率
1.63
信息比率
1.14
贝塔
0.85
年化波动率
0.68
最大回撤
48.80%
绝对收益 相对收益 对数收益
补充
跨交易所之间:binance的BTC/USDT 和 huobi的BTC/USDT 之间统计套利之配对交易
隐藏输出
data1 = api.coinbar(symbol='BTCUSDT',exchange='binance',freq='daily',start_date='2019-01-01',end_date='2019-12-01')['close']
data2 = api.coinbar(symbol='BTCUSDT',exchange='huobi',freq='daily',start_date='2019-01-01',end_date='2019-12-01')['close']
x=np.array(data1)
y=np.array(data2)
zz=pd.concat([testStationarity(x),testStationarity(y)],axis=1)
zz.columns=['binance.btcusdt','binance.ethusdt']
diffx=data1.diff(1)
diffx.dropna(inplace=True)
diffx=np.array(diffx)
diffy=data2.diff(1)
diffy.dropna(inplace=True)
diffy=np.array(diffy)
tz=pd.concat([testStationarity(diffx),testStationarity(diffy)],axis=1)
zz.columns=['binance.btcusdt','binance.ethusdt']
mean=(data1-data2).mean()
std=(data1-data2).std()
s1=pd.Series(mean,index=range(len(data1)))
s2=pd.Series(mean+std,index=range(len(data1)))
s3=pd.Series(mean-std,index=range(len(data1)))
data3=pd.concat([data1-data2,s1,s2,s3],axis=1)
data3.columns=['spreadprice','mean','upper','down']
data3.plot(figsize=(14,7))
print(mean+std,mean-std)
plt.grid()
plt.show()
178.191381016475 -155.72850676497794
|
|