程序员大牛「手把手教你」使用Python实现统计套利
一、交易对象选取
为了说明螺纹钢商品期货市场的跨期套利机制及其应用价值,在分析研究过程中我们选择了两个不同到期月份的同一品种的商品期货合约作为分析样本。
相关性检验
利用某个第三方平台的数据爬虫工具自动化获取螺纹钢期货品种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']
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的同学,大家可以+下Python学习分享裙:五二八 三九七 六一七,即可免费领取一整套系统的 Python学习教程!

从价格的走势图中可观察到rb1903和rb1904以及rb1908和rb1907的价格走势之间显示出较强的相关性,并绘制出它们之间的相关矩阵
sns.heatmap(df.corr(), annot=True, square=True)
plt.show()

我们推测,在分析结果中发现一组数据与其他一组数据之间显示出较高的相关性;具体而言,在这四组数据中存在明显强于其他组合的趋势,并且基于此判断结果选择这对变量进行配对交易。
ADF检验
下面对这两组数据进行平稳性检验。
导入自statsmodels库中的adfuller函数。
定义一个名为check的函数。
通过pandas库读取本地CSV文件data.csv的内容到数据框df中。
将数据框df中列名rb1907的值赋值给变量price_A。
将数据框df中列名rb1908的值赋值给变量price_B。
运行adfuller函数并传递price_A参数。
=
运行adfuller函数并传递price_B参数。
=
输出结果A
=
输出结果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统计量的数值超过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)
观察结果显示,在对原始数据进行一阶差分处理后得到的数据呈现平稳特性。这表明原始数据呈现出一阶单整特征,并且满足进行协intest检验的前提条件。因此,在此阶段我们建议对这两组数据进行协intest检验分析以便探究两者之间是否存在协intest关系
协整检验
导入自statsmodels库中的coint函数。
定义名为check的函数。
读取CSV文件./data.csv中的数据存入变量df中。
从df中提取列'rb1907'的值赋值给price_A。
从df中提取列'rb1908'的值赋值给price_B。
执行coint函数并输出结果。
(-3.6104387172088277, 0.p-value= , array([-3.p46p24pppppp, -3.prpolongsss, -3......))
结果看出 t-statistic 小于5%,所以说有95%的把握说两者具有协整关系。
二、主体策略
本文旨在构建配对交易策略,并探讨其中套利的关键要素。具体而言,在保证该对冲策略具有市场中性方面存在核心要求:即无论市场趋势呈现上升或下降状态,在这种情况下所采取的所有措施均能使得所预期的收益保持正值。
投资组合的构建
配对交易的核心关注点在于两种资产价格之间的偏差。根据均值回归理论,在股票市场、期货市场以及其他金融衍生品交易领域中,无论价格处于高于还是低于价值中枢(即均值)的状态下都存在较高的概率趋向于价值中枢的方向发展。因此,在这两组数据呈现出协整关系时(即两者之间存在长期稳定的比例关系),当它们两者的价差高于均值水平时,则会趋向于向均值回归;反之亦然。
下面得到去中心化后的价差序列:
执行策略
获取RB1907期货价格序列
执行策略
读取数据
计算价格差异
标准化价差
绘图展示

特别关注的是 A 和 B 的价差,在套利策略中常会使用 B 的价格乘以一个协整系数,在这种情况下其研究对象通常是这些残差。协整检验的结果表明这些残差显示出平稳性特征。因此更适合运用均值回归理论
设置开仓和止损的阈值
为了便于比较开仓与止损的阈值,在分析窗口内数据波动性时
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()

三、历史回测
下面就以样本内 数据进行回测一下:
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()
收益结果如下:

经回测验证的结果并不令人满意,并非因为我们缺乏参数优化这一关键环节;从前面分析可知,在统计套利策略中每一次的操作盈利较为有限。其主要原因不仅在于价差所带来的一种相对较低水平的收益,并且还与在止损门槛设置方面也存在明显不足有关。由此可见,在统计套利策略中合理地设定止损阈值具有至关重要的作用。
四、注意
为提高操作效率,在本研究中涉及的策略构建过程及历史回测分析均基于样本内部的数据完成。而在实际应用中,则需要将模型分为训练集与验证集两部分,并对模型性能进行外部验证。
在挑选配对数据的品种时,则需同时关注其间的关联性之外,并需注意其市场流动性的特征等。
3、历史回测时,还需要将手续费、滑点等因素考虑进去。
关于Python金融量化
本文旨在为投资界的专业人士提供关于Python在金融量化领域的深入分享与学习机会。通过加入知识星球平台(加入后即可领取约30G的量化投资视频资料),您将获得丰富的资源包包括:约30G的量化金融PDF资料库、全面的Python代码库以及深度学习框架指南;同时还能参与定期的学习交流群组,在线与博主进行实时互动交流,并与其他行业专家建立专业人脉。
