'「手把手教你」使用Python實現統計套利'

Python JSON 期貨 社交網絡 新浪 Python金融量化 2019-08-27
"

文章轉載自公眾號 人工智能量化實驗室 , 作者 Frankie的賬號

一、交易對象選取

我們以商品期貨市場的螺紋鋼品種的跨期套利為例,選取兩組不同到期月份的同種商品期貨合約作為交易對象。

相關性檢驗

通過新浪財經的期貨數據接口爬取螺紋鋼rb1903到rb1908的六組數據,先看一下它們的走勢:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import urllib.request as urllib2
import json
def findPairs():
ids = ['rb1903', 'rb1904', 'rb1905', 'rb1906', 'rb1907', 'rb1908']
url_5m = 'http://stock2.finance.sina.com.cn/futures/api/json.php/IndexService.getInnerFuturesMiniKLine5m?symbol='
result = []
for id in ids:
url = url_5m + id
req = urllib2.Request(url)
rsp = urllib2.urlopen(req)
res = rsp.read()
res_json = json.loads(res)
result.append(res_json)
close_result = []
for instrument in result:
oneDay_list = []
for oneDay in instrument:
oneDay_list.append(float(oneDay[-2]))
close_result.append(np.array(oneDay_list))
close_result = np.array(close_result)
close_result = close_result.T
df = pd.DataFrame(data=close_result, columns=ids)
df.plot()
plt.show()
"

文章轉載自公眾號 人工智能量化實驗室 , 作者 Frankie的賬號

一、交易對象選取

我們以商品期貨市場的螺紋鋼品種的跨期套利為例,選取兩組不同到期月份的同種商品期貨合約作為交易對象。

相關性檢驗

通過新浪財經的期貨數據接口爬取螺紋鋼rb1903到rb1908的六組數據,先看一下它們的走勢:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import urllib.request as urllib2
import json
def findPairs():
ids = ['rb1903', 'rb1904', 'rb1905', 'rb1906', 'rb1907', 'rb1908']
url_5m = 'http://stock2.finance.sina.com.cn/futures/api/json.php/IndexService.getInnerFuturesMiniKLine5m?symbol='
result = []
for id in ids:
url = url_5m + id
req = urllib2.Request(url)
rsp = urllib2.urlopen(req)
res = rsp.read()
res_json = json.loads(res)
result.append(res_json)
close_result = []
for instrument in result:
oneDay_list = []
for oneDay in instrument:
oneDay_list.append(float(oneDay[-2]))
close_result.append(np.array(oneDay_list))
close_result = np.array(close_result)
close_result = close_result.T
df = pd.DataFrame(data=close_result, columns=ids)
df.plot()
plt.show()
「手把手教你」使用Python實現統計套利

從價格的走勢圖中,可以看出 rb1903 和 rb1904 以及 rb1908 和 rb1907 的走勢上存在很強的相關性,下面畫出它們之間的相關矩陣。

 sns.heatmap(df.corr(), annot=True, square=True)
plt.show()
"

文章轉載自公眾號 人工智能量化實驗室 , 作者 Frankie的賬號

一、交易對象選取

我們以商品期貨市場的螺紋鋼品種的跨期套利為例,選取兩組不同到期月份的同種商品期貨合約作為交易對象。

相關性檢驗

通過新浪財經的期貨數據接口爬取螺紋鋼rb1903到rb1908的六組數據,先看一下它們的走勢:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import urllib.request as urllib2
import json
def findPairs():
ids = ['rb1903', 'rb1904', 'rb1905', 'rb1906', 'rb1907', 'rb1908']
url_5m = 'http://stock2.finance.sina.com.cn/futures/api/json.php/IndexService.getInnerFuturesMiniKLine5m?symbol='
result = []
for id in ids:
url = url_5m + id
req = urllib2.Request(url)
rsp = urllib2.urlopen(req)
res = rsp.read()
res_json = json.loads(res)
result.append(res_json)
close_result = []
for instrument in result:
oneDay_list = []
for oneDay in instrument:
oneDay_list.append(float(oneDay[-2]))
close_result.append(np.array(oneDay_list))
close_result = np.array(close_result)
close_result = close_result.T
df = pd.DataFrame(data=close_result, columns=ids)
df.plot()
plt.show()
「手把手教你」使用Python實現統計套利

從價格的走勢圖中,可以看出 rb1903 和 rb1904 以及 rb1908 和 rb1907 的走勢上存在很強的相關性,下面畫出它們之間的相關矩陣。

 sns.heatmap(df.corr(), annot=True, square=True)
plt.show()
「手把手教你」使用Python實現統計套利

正如我們所推斷的, rb1903 和 rb1904 以及 rb1908 和 rb1907這兩組之間具有很強的相關性,其中,rb1907 和 rb1908 之間的相關程度最大,所以下面我們將選取 rb1907 和 rb1908作為配對交易的品種。

ADF檢驗

下面對這兩組數據進行平穩性檢驗。

from statsmodels.tsa.stattools import adfuller
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-1.7605803524947852, 0.4002005032657946, 3, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1750.3205777927317)
(-1.6918211072949225, 0.4353313388810546, 2, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1776.486392805771)

從結果可以看出 t-statistic 的值要大於10%,所以說無法拒絕原假設,也就是原數據都是非平穩的。

下面進行一階差分之後檢查一下:

def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
price_A = np.diff(price_A)
price_B = np.diff(price_B)
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-7.519664365222082, 3.820429924735319e-11, 2, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1744.3991445433894)
(-9.917570016245815, 3.051148786023717e-17, 1, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1770.1154237195128)

結果可以看出,一階差分之後的數據是平穩的,也就是說原數據是一階單整的,滿足協整關係的前提,所以下一步我們對這兩組數據進行協整檢驗,來探究兩者是否是協整的。

協整檢驗

from statsmodels.tsa.stattools import coint
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
print(coint(price_A, price_B))
(-3.6104387172088277, 0.02378223384906601, array([-3.94246081, -3.36160059, -3.06209517]))

結果看出 t-statistic 小於5%,所以說有95%的把握說兩者具有協整關係。

二、主體策略

下面將構建配對交易的策略,統計套利的關鍵是要保證策略的市場中性,也就是說無論市場的趨勢是上升還是下降,都要使策略或者預期的收益為正。

投資組合的構建

配對交易主要分析的對象是兩個品種價格之間的偏離,由均值迴歸理論知,在股票、期貨或者其他金融衍生品的交易市場中,無論高於或低於價值中樞(或均值)都有很高的概率向價值中樞迴歸的趨勢。所以說,在具有協整關係的這兩組數據中,當它們兩者的價差高與均值時則會有向低走的趨勢,價差低於均值時則會有向高走的趨勢。

下面得到去中心化後的價差序列:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
plt.show()
"

文章轉載自公眾號 人工智能量化實驗室 , 作者 Frankie的賬號

一、交易對象選取

我們以商品期貨市場的螺紋鋼品種的跨期套利為例,選取兩組不同到期月份的同種商品期貨合約作為交易對象。

相關性檢驗

通過新浪財經的期貨數據接口爬取螺紋鋼rb1903到rb1908的六組數據,先看一下它們的走勢:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import urllib.request as urllib2
import json
def findPairs():
ids = ['rb1903', 'rb1904', 'rb1905', 'rb1906', 'rb1907', 'rb1908']
url_5m = 'http://stock2.finance.sina.com.cn/futures/api/json.php/IndexService.getInnerFuturesMiniKLine5m?symbol='
result = []
for id in ids:
url = url_5m + id
req = urllib2.Request(url)
rsp = urllib2.urlopen(req)
res = rsp.read()
res_json = json.loads(res)
result.append(res_json)
close_result = []
for instrument in result:
oneDay_list = []
for oneDay in instrument:
oneDay_list.append(float(oneDay[-2]))
close_result.append(np.array(oneDay_list))
close_result = np.array(close_result)
close_result = close_result.T
df = pd.DataFrame(data=close_result, columns=ids)
df.plot()
plt.show()
「手把手教你」使用Python實現統計套利

從價格的走勢圖中,可以看出 rb1903 和 rb1904 以及 rb1908 和 rb1907 的走勢上存在很強的相關性,下面畫出它們之間的相關矩陣。

 sns.heatmap(df.corr(), annot=True, square=True)
plt.show()
「手把手教你」使用Python實現統計套利

正如我們所推斷的, rb1903 和 rb1904 以及 rb1908 和 rb1907這兩組之間具有很強的相關性,其中,rb1907 和 rb1908 之間的相關程度最大,所以下面我們將選取 rb1907 和 rb1908作為配對交易的品種。

ADF檢驗

下面對這兩組數據進行平穩性檢驗。

from statsmodels.tsa.stattools import adfuller
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-1.7605803524947852, 0.4002005032657946, 3, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1750.3205777927317)
(-1.6918211072949225, 0.4353313388810546, 2, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1776.486392805771)

從結果可以看出 t-statistic 的值要大於10%,所以說無法拒絕原假設,也就是原數據都是非平穩的。

下面進行一階差分之後檢查一下:

def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
price_A = np.diff(price_A)
price_B = np.diff(price_B)
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-7.519664365222082, 3.820429924735319e-11, 2, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1744.3991445433894)
(-9.917570016245815, 3.051148786023717e-17, 1, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1770.1154237195128)

結果可以看出,一階差分之後的數據是平穩的,也就是說原數據是一階單整的,滿足協整關係的前提,所以下一步我們對這兩組數據進行協整檢驗,來探究兩者是否是協整的。

協整檢驗

from statsmodels.tsa.stattools import coint
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
print(coint(price_A, price_B))
(-3.6104387172088277, 0.02378223384906601, array([-3.94246081, -3.36160059, -3.06209517]))

結果看出 t-statistic 小於5%,所以說有95%的把握說兩者具有協整關係。

二、主體策略

下面將構建配對交易的策略,統計套利的關鍵是要保證策略的市場中性,也就是說無論市場的趨勢是上升還是下降,都要使策略或者預期的收益為正。

投資組合的構建

配對交易主要分析的對象是兩個品種價格之間的偏離,由均值迴歸理論知,在股票、期貨或者其他金融衍生品的交易市場中,無論高於或低於價值中樞(或均值)都有很高的概率向價值中樞迴歸的趨勢。所以說,在具有協整關係的這兩組數據中,當它們兩者的價差高與均值時則會有向低走的趨勢,價差低於均值時則會有向高走的趨勢。

下面得到去中心化後的價差序列:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
plt.show()
「手把手教你」使用Python實現統計套利

注意這裡直接研究的是 A、B 價格差值,統計套利策略中通常會將 B 價格乘以一個協整係數,研究的對象是它們的殘差,由於協整檢驗後可以知道它們的殘差具有平穩性,所以更好的應用均值迴歸的理論。

設置開倉和止損的閾值

為了使開倉和止損的閾值更好地比較,所以就將開倉閾值設置為窗口內數據的兩倍標準差,止損設置為三倍標準差。這個標準差的倍數可以通過調參來不斷調優,標準差的設置也可以通過 GARCH 等模型擬合的自迴歸條件異方差類似的時變標準差來代替。

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
sigma = np.std(mspread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
ax.hlines(2 * sigma, 0, len(mspread), colors='b')
ax.hlines(-2 * sigma, 0, len(mspread), colors='b')
ax.hlines(3 * sigma, 0, len(mspread), colors='r')
ax.hlines(-3 * sigma, 0, len(mspread), colors='r')
plt.show()
"

文章轉載自公眾號 人工智能量化實驗室 , 作者 Frankie的賬號

一、交易對象選取

我們以商品期貨市場的螺紋鋼品種的跨期套利為例,選取兩組不同到期月份的同種商品期貨合約作為交易對象。

相關性檢驗

通過新浪財經的期貨數據接口爬取螺紋鋼rb1903到rb1908的六組數據,先看一下它們的走勢:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import urllib.request as urllib2
import json
def findPairs():
ids = ['rb1903', 'rb1904', 'rb1905', 'rb1906', 'rb1907', 'rb1908']
url_5m = 'http://stock2.finance.sina.com.cn/futures/api/json.php/IndexService.getInnerFuturesMiniKLine5m?symbol='
result = []
for id in ids:
url = url_5m + id
req = urllib2.Request(url)
rsp = urllib2.urlopen(req)
res = rsp.read()
res_json = json.loads(res)
result.append(res_json)
close_result = []
for instrument in result:
oneDay_list = []
for oneDay in instrument:
oneDay_list.append(float(oneDay[-2]))
close_result.append(np.array(oneDay_list))
close_result = np.array(close_result)
close_result = close_result.T
df = pd.DataFrame(data=close_result, columns=ids)
df.plot()
plt.show()
「手把手教你」使用Python實現統計套利

從價格的走勢圖中,可以看出 rb1903 和 rb1904 以及 rb1908 和 rb1907 的走勢上存在很強的相關性,下面畫出它們之間的相關矩陣。

 sns.heatmap(df.corr(), annot=True, square=True)
plt.show()
「手把手教你」使用Python實現統計套利

正如我們所推斷的, rb1903 和 rb1904 以及 rb1908 和 rb1907這兩組之間具有很強的相關性,其中,rb1907 和 rb1908 之間的相關程度最大,所以下面我們將選取 rb1907 和 rb1908作為配對交易的品種。

ADF檢驗

下面對這兩組數據進行平穩性檢驗。

from statsmodels.tsa.stattools import adfuller
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-1.7605803524947852, 0.4002005032657946, 3, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1750.3205777927317)
(-1.6918211072949225, 0.4353313388810546, 2, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1776.486392805771)

從結果可以看出 t-statistic 的值要大於10%,所以說無法拒絕原假設,也就是原數據都是非平穩的。

下面進行一階差分之後檢查一下:

def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
price_A = np.diff(price_A)
price_B = np.diff(price_B)
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-7.519664365222082, 3.820429924735319e-11, 2, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1744.3991445433894)
(-9.917570016245815, 3.051148786023717e-17, 1, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1770.1154237195128)

結果可以看出,一階差分之後的數據是平穩的,也就是說原數據是一階單整的,滿足協整關係的前提,所以下一步我們對這兩組數據進行協整檢驗,來探究兩者是否是協整的。

協整檢驗

from statsmodels.tsa.stattools import coint
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
print(coint(price_A, price_B))
(-3.6104387172088277, 0.02378223384906601, array([-3.94246081, -3.36160059, -3.06209517]))

結果看出 t-statistic 小於5%,所以說有95%的把握說兩者具有協整關係。

二、主體策略

下面將構建配對交易的策略,統計套利的關鍵是要保證策略的市場中性,也就是說無論市場的趨勢是上升還是下降,都要使策略或者預期的收益為正。

投資組合的構建

配對交易主要分析的對象是兩個品種價格之間的偏離,由均值迴歸理論知,在股票、期貨或者其他金融衍生品的交易市場中,無論高於或低於價值中樞(或均值)都有很高的概率向價值中樞迴歸的趨勢。所以說,在具有協整關係的這兩組數據中,當它們兩者的價差高與均值時則會有向低走的趨勢,價差低於均值時則會有向高走的趨勢。

下面得到去中心化後的價差序列:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
plt.show()
「手把手教你」使用Python實現統計套利

注意這裡直接研究的是 A、B 價格差值,統計套利策略中通常會將 B 價格乘以一個協整係數,研究的對象是它們的殘差,由於協整檢驗後可以知道它們的殘差具有平穩性,所以更好的應用均值迴歸的理論。

設置開倉和止損的閾值

為了使開倉和止損的閾值更好地比較,所以就將開倉閾值設置為窗口內數據的兩倍標準差,止損設置為三倍標準差。這個標準差的倍數可以通過調參來不斷調優,標準差的設置也可以通過 GARCH 等模型擬合的自迴歸條件異方差類似的時變標準差來代替。

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
sigma = np.std(mspread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
ax.hlines(2 * sigma, 0, len(mspread), colors='b')
ax.hlines(-2 * sigma, 0, len(mspread), colors='b')
ax.hlines(3 * sigma, 0, len(mspread), colors='r')
ax.hlines(-3 * sigma, 0, len(mspread), colors='r')
plt.show()
「手把手教你」使用Python實現統計套利

三、歷史回測

下面就以樣本內數據進行回測一下:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
sigma = np.std(mspread)
open = 2 * sigma
stop = 3 * sigma
profit_list = []
hold = False
hold_price_A = 0
hold_price_B = 0
hold_state = 0 # 1 (A:long B:short) -1 (A:short B:long)
profit = 0
for i in range(len(price_A)):
if hold == False:
if mspread[i] >= open:
hold_price_A = price_A[i]
hold_price_B = price_B[i]
hold_state = -1
hold = True
elif mspread[i] <= -open:
hold_price_A = price_A[i]
hold_price_B = price_B[i]
hold_state = -1
hold = True
else:
if mspread[i] >= stop and hold_state == -1 :
profit = (hold_price_A - price_A[i]) + (price_B[i] - hold_price_B)
hold_state = 0
hold = False
if mspread[i] <= -stop and hold_state == 1 :
profit = (price_A[i] - hold_price_A) + (hold_price_B - price_B[i])
hold_state = 0
hold = False
if mspread[i] <= 0 and hold_state == -1:
profit = (hold_price_A - price_A[i]) + (price_B[i] - hold_price_B)
hold_state = 0
hold = False
if mspread[i] >= 0 and hold_state == 1:
profit = (price_A[i] - hold_price_A) + (hold_price_B - price_B[i])
hold_state = 0
hold = False
profit_list.append(profit)
print(profit_list)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(profit_list)), profit_list)
plt.show()

收益結果如下:

"

文章轉載自公眾號 人工智能量化實驗室 , 作者 Frankie的賬號

一、交易對象選取

我們以商品期貨市場的螺紋鋼品種的跨期套利為例,選取兩組不同到期月份的同種商品期貨合約作為交易對象。

相關性檢驗

通過新浪財經的期貨數據接口爬取螺紋鋼rb1903到rb1908的六組數據,先看一下它們的走勢:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import urllib.request as urllib2
import json
def findPairs():
ids = ['rb1903', 'rb1904', 'rb1905', 'rb1906', 'rb1907', 'rb1908']
url_5m = 'http://stock2.finance.sina.com.cn/futures/api/json.php/IndexService.getInnerFuturesMiniKLine5m?symbol='
result = []
for id in ids:
url = url_5m + id
req = urllib2.Request(url)
rsp = urllib2.urlopen(req)
res = rsp.read()
res_json = json.loads(res)
result.append(res_json)
close_result = []
for instrument in result:
oneDay_list = []
for oneDay in instrument:
oneDay_list.append(float(oneDay[-2]))
close_result.append(np.array(oneDay_list))
close_result = np.array(close_result)
close_result = close_result.T
df = pd.DataFrame(data=close_result, columns=ids)
df.plot()
plt.show()
「手把手教你」使用Python實現統計套利

從價格的走勢圖中,可以看出 rb1903 和 rb1904 以及 rb1908 和 rb1907 的走勢上存在很強的相關性,下面畫出它們之間的相關矩陣。

 sns.heatmap(df.corr(), annot=True, square=True)
plt.show()
「手把手教你」使用Python實現統計套利

正如我們所推斷的, rb1903 和 rb1904 以及 rb1908 和 rb1907這兩組之間具有很強的相關性,其中,rb1907 和 rb1908 之間的相關程度最大,所以下面我們將選取 rb1907 和 rb1908作為配對交易的品種。

ADF檢驗

下面對這兩組數據進行平穩性檢驗。

from statsmodels.tsa.stattools import adfuller
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-1.7605803524947852, 0.4002005032657946, 3, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1750.3205777927317)
(-1.6918211072949225, 0.4353313388810546, 2, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1776.486392805771)

從結果可以看出 t-statistic 的值要大於10%,所以說無法拒絕原假設,也就是原數據都是非平穩的。

下面進行一階差分之後檢查一下:

def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
price_A = np.diff(price_A)
price_B = np.diff(price_B)
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-7.519664365222082, 3.820429924735319e-11, 2, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1744.3991445433894)
(-9.917570016245815, 3.051148786023717e-17, 1, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1770.1154237195128)

結果可以看出,一階差分之後的數據是平穩的,也就是說原數據是一階單整的,滿足協整關係的前提,所以下一步我們對這兩組數據進行協整檢驗,來探究兩者是否是協整的。

協整檢驗

from statsmodels.tsa.stattools import coint
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
print(coint(price_A, price_B))
(-3.6104387172088277, 0.02378223384906601, array([-3.94246081, -3.36160059, -3.06209517]))

結果看出 t-statistic 小於5%,所以說有95%的把握說兩者具有協整關係。

二、主體策略

下面將構建配對交易的策略,統計套利的關鍵是要保證策略的市場中性,也就是說無論市場的趨勢是上升還是下降,都要使策略或者預期的收益為正。

投資組合的構建

配對交易主要分析的對象是兩個品種價格之間的偏離,由均值迴歸理論知,在股票、期貨或者其他金融衍生品的交易市場中,無論高於或低於價值中樞(或均值)都有很高的概率向價值中樞迴歸的趨勢。所以說,在具有協整關係的這兩組數據中,當它們兩者的價差高與均值時則會有向低走的趨勢,價差低於均值時則會有向高走的趨勢。

下面得到去中心化後的價差序列:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
plt.show()
「手把手教你」使用Python實現統計套利

注意這裡直接研究的是 A、B 價格差值,統計套利策略中通常會將 B 價格乘以一個協整係數,研究的對象是它們的殘差,由於協整檢驗後可以知道它們的殘差具有平穩性,所以更好的應用均值迴歸的理論。

設置開倉和止損的閾值

為了使開倉和止損的閾值更好地比較,所以就將開倉閾值設置為窗口內數據的兩倍標準差,止損設置為三倍標準差。這個標準差的倍數可以通過調參來不斷調優,標準差的設置也可以通過 GARCH 等模型擬合的自迴歸條件異方差類似的時變標準差來代替。

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
sigma = np.std(mspread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
ax.hlines(2 * sigma, 0, len(mspread), colors='b')
ax.hlines(-2 * sigma, 0, len(mspread), colors='b')
ax.hlines(3 * sigma, 0, len(mspread), colors='r')
ax.hlines(-3 * sigma, 0, len(mspread), colors='r')
plt.show()
「手把手教你」使用Python實現統計套利

三、歷史回測

下面就以樣本內數據進行回測一下:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
sigma = np.std(mspread)
open = 2 * sigma
stop = 3 * sigma
profit_list = []
hold = False
hold_price_A = 0
hold_price_B = 0
hold_state = 0 # 1 (A:long B:short) -1 (A:short B:long)
profit = 0
for i in range(len(price_A)):
if hold == False:
if mspread[i] >= open:
hold_price_A = price_A[i]
hold_price_B = price_B[i]
hold_state = -1
hold = True
elif mspread[i] <= -open:
hold_price_A = price_A[i]
hold_price_B = price_B[i]
hold_state = -1
hold = True
else:
if mspread[i] >= stop and hold_state == -1 :
profit = (hold_price_A - price_A[i]) + (price_B[i] - hold_price_B)
hold_state = 0
hold = False
if mspread[i] <= -stop and hold_state == 1 :
profit = (price_A[i] - hold_price_A) + (hold_price_B - price_B[i])
hold_state = 0
hold = False
if mspread[i] <= 0 and hold_state == -1:
profit = (hold_price_A - price_A[i]) + (price_B[i] - hold_price_B)
hold_state = 0
hold = False
if mspread[i] >= 0 and hold_state == 1:
profit = (price_A[i] - hold_price_A) + (hold_price_B - price_B[i])
hold_state = 0
hold = False
profit_list.append(profit)
print(profit_list)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(profit_list)), profit_list)
plt.show()

收益結果如下:

「手把手教你」使用Python實現統計套利

可以看出回測結果是很不盡人意的,因為我們並沒有對參數進行調優,從前面可以知道統計套利單次的收益是比較薄弱的,主要原因不僅僅是價差帶來的這種相對收益本來就比較低,還有就是止損閾值設置的問題,有時一次止損就會cover掉之前所有的收益。所以說在統計套利中,閾值的設置是非常重要的。

四、注意

1、為了方便操作,以上實驗的策略構建以及歷史回測都是樣本內進行測試的,真正的策略回測要劃分訓練數據和測試數據,進行樣本外測試。

2、在選擇配對數據的品種時,除了要考慮配對品種的相關性之外,還要考慮品種的市場流動性等因素。

3、歷史回測時,還需要將手續費、滑點等因素考慮進去。

關於Python金融量化

專注於分享Python在金融量化領域的應用。加入知識星球,可以免費獲取30多g的量化投資視頻資料、量化金融相關PDF資料、公眾號文章Python完整源碼、量化投資前沿分析框架,與博主直接交流、結識圈內朋友等。

"

文章轉載自公眾號 人工智能量化實驗室 , 作者 Frankie的賬號

一、交易對象選取

我們以商品期貨市場的螺紋鋼品種的跨期套利為例,選取兩組不同到期月份的同種商品期貨合約作為交易對象。

相關性檢驗

通過新浪財經的期貨數據接口爬取螺紋鋼rb1903到rb1908的六組數據,先看一下它們的走勢:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import urllib.request as urllib2
import json
def findPairs():
ids = ['rb1903', 'rb1904', 'rb1905', 'rb1906', 'rb1907', 'rb1908']
url_5m = 'http://stock2.finance.sina.com.cn/futures/api/json.php/IndexService.getInnerFuturesMiniKLine5m?symbol='
result = []
for id in ids:
url = url_5m + id
req = urllib2.Request(url)
rsp = urllib2.urlopen(req)
res = rsp.read()
res_json = json.loads(res)
result.append(res_json)
close_result = []
for instrument in result:
oneDay_list = []
for oneDay in instrument:
oneDay_list.append(float(oneDay[-2]))
close_result.append(np.array(oneDay_list))
close_result = np.array(close_result)
close_result = close_result.T
df = pd.DataFrame(data=close_result, columns=ids)
df.plot()
plt.show()
「手把手教你」使用Python實現統計套利

從價格的走勢圖中,可以看出 rb1903 和 rb1904 以及 rb1908 和 rb1907 的走勢上存在很強的相關性,下面畫出它們之間的相關矩陣。

 sns.heatmap(df.corr(), annot=True, square=True)
plt.show()
「手把手教你」使用Python實現統計套利

正如我們所推斷的, rb1903 和 rb1904 以及 rb1908 和 rb1907這兩組之間具有很強的相關性,其中,rb1907 和 rb1908 之間的相關程度最大,所以下面我們將選取 rb1907 和 rb1908作為配對交易的品種。

ADF檢驗

下面對這兩組數據進行平穩性檢驗。

from statsmodels.tsa.stattools import adfuller
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-1.7605803524947852, 0.4002005032657946, 3, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1750.3205777927317)
(-1.6918211072949225, 0.4353313388810546, 2, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1776.486392805771)

從結果可以看出 t-statistic 的值要大於10%,所以說無法拒絕原假設,也就是原數據都是非平穩的。

下面進行一階差分之後檢查一下:

def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
price_A = np.diff(price_A)
price_B = np.diff(price_B)
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(result_A)
print(result_B)

結果如下:

(-7.519664365222082, 3.820429924735319e-11, 2, 238, {'1%': -3.458128284586202, '5%': -2.873761835239286, '10%': -2.5732834559706235}, 1744.3991445433894)
(-9.917570016245815, 3.051148786023717e-17, 1, 239, {'1%': -3.458010773719797, '5%': -2.8737103617125186, '10%': -2.5732559963936206}, 1770.1154237195128)

結果可以看出,一階差分之後的數據是平穩的,也就是說原數據是一階單整的,滿足協整關係的前提,所以下一步我們對這兩組數據進行協整檢驗,來探究兩者是否是協整的。

協整檢驗

from statsmodels.tsa.stattools import coint
def check():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
print(coint(price_A, price_B))
(-3.6104387172088277, 0.02378223384906601, array([-3.94246081, -3.36160059, -3.06209517]))

結果看出 t-statistic 小於5%,所以說有95%的把握說兩者具有協整關係。

二、主體策略

下面將構建配對交易的策略,統計套利的關鍵是要保證策略的市場中性,也就是說無論市場的趨勢是上升還是下降,都要使策略或者預期的收益為正。

投資組合的構建

配對交易主要分析的對象是兩個品種價格之間的偏離,由均值迴歸理論知,在股票、期貨或者其他金融衍生品的交易市場中,無論高於或低於價值中樞(或均值)都有很高的概率向價值中樞迴歸的趨勢。所以說,在具有協整關係的這兩組數據中,當它們兩者的價差高與均值時則會有向低走的趨勢,價差低於均值時則會有向高走的趨勢。

下面得到去中心化後的價差序列:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
plt.show()
「手把手教你」使用Python實現統計套利

注意這裡直接研究的是 A、B 價格差值,統計套利策略中通常會將 B 價格乘以一個協整係數,研究的對象是它們的殘差,由於協整檢驗後可以知道它們的殘差具有平穩性,所以更好的應用均值迴歸的理論。

設置開倉和止損的閾值

為了使開倉和止損的閾值更好地比較,所以就將開倉閾值設置為窗口內數據的兩倍標準差,止損設置為三倍標準差。這個標準差的倍數可以通過調參來不斷調優,標準差的設置也可以通過 GARCH 等模型擬合的自迴歸條件異方差類似的時變標準差來代替。

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
sigma = np.std(mspread)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(mspread)), mspread)
ax.hlines(0, 0, len(mspread))
ax.hlines(2 * sigma, 0, len(mspread), colors='b')
ax.hlines(-2 * sigma, 0, len(mspread), colors='b')
ax.hlines(3 * sigma, 0, len(mspread), colors='r')
ax.hlines(-3 * sigma, 0, len(mspread), colors='r')
plt.show()
「手把手教你」使用Python實現統計套利

三、歷史回測

下面就以樣本內數據進行回測一下:

def strategy():
df = pd.read_csv('./data.csv')
price_A = df['rb1907'].values
price_B = df['rb1908'].values
spread = price_A - price_B
mspread = spread - np.mean(spread)
sigma = np.std(mspread)
open = 2 * sigma
stop = 3 * sigma
profit_list = []
hold = False
hold_price_A = 0
hold_price_B = 0
hold_state = 0 # 1 (A:long B:short) -1 (A:short B:long)
profit = 0
for i in range(len(price_A)):
if hold == False:
if mspread[i] >= open:
hold_price_A = price_A[i]
hold_price_B = price_B[i]
hold_state = -1
hold = True
elif mspread[i] <= -open:
hold_price_A = price_A[i]
hold_price_B = price_B[i]
hold_state = -1
hold = True
else:
if mspread[i] >= stop and hold_state == -1 :
profit = (hold_price_A - price_A[i]) + (price_B[i] - hold_price_B)
hold_state = 0
hold = False
if mspread[i] <= -stop and hold_state == 1 :
profit = (price_A[i] - hold_price_A) + (hold_price_B - price_B[i])
hold_state = 0
hold = False
if mspread[i] <= 0 and hold_state == -1:
profit = (hold_price_A - price_A[i]) + (price_B[i] - hold_price_B)
hold_state = 0
hold = False
if mspread[i] >= 0 and hold_state == 1:
profit = (price_A[i] - hold_price_A) + (hold_price_B - price_B[i])
hold_state = 0
hold = False
profit_list.append(profit)
print(profit_list)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(len(profit_list)), profit_list)
plt.show()

收益結果如下:

「手把手教你」使用Python實現統計套利

可以看出回測結果是很不盡人意的,因為我們並沒有對參數進行調優,從前面可以知道統計套利單次的收益是比較薄弱的,主要原因不僅僅是價差帶來的這種相對收益本來就比較低,還有就是止損閾值設置的問題,有時一次止損就會cover掉之前所有的收益。所以說在統計套利中,閾值的設置是非常重要的。

四、注意

1、為了方便操作,以上實驗的策略構建以及歷史回測都是樣本內進行測試的,真正的策略回測要劃分訓練數據和測試數據,進行樣本外測試。

2、在選擇配對數據的品種時,除了要考慮配對品種的相關性之外,還要考慮品種的市場流動性等因素。

3、歷史回測時,還需要將手續費、滑點等因素考慮進去。

關於Python金融量化

專注於分享Python在金融量化領域的應用。加入知識星球,可以免費獲取30多g的量化投資視頻資料、量化金融相關PDF資料、公眾號文章Python完整源碼、量化投資前沿分析框架,與博主直接交流、結識圈內朋友等。

「手把手教你」使用Python實現統計套利

"

相關推薦

推薦中...