高频交易——当期货配对交易加入了止损

idea

目前在配对交易的识别上比较有代表性的是根据二者的相关性来判断,也有根据标的之间协整性来进行选择的,在这里我们结合两种方式进行综合选取,即在相关性较高的合约对之中选取具有显著协整性关系的合约对,并设置一定的止损率进行投资策略的改进。

数据介绍

我们有38只期货合约的tick级快照数据,每只合约的数据如下:
合约数据明细
其中的数据时间戳为100纳秒数据,并且开始于0001年1月1日,因此在这里将其转化为现实数据,并转化为分钟数据:
在这里插入图片描述
最终的每个合约的分钟数据如下,基于此数据进行配对交易。
分钟数据明细

配对交易

寻找配对标的

相关性

计算合约标的中国两两之间的相关性大小:
在这里插入图片描述
OK,合约太多看不清楚,我们选取部分结果展示如下,在这里不变透露合约名称,因此用合约id代表:
在这里插入图片描述
在这里我们应该选取相关性较高的几对合约,选取阈值为0.7,即选取相关性大于0.7的合约对储存于变量pairs:
在这里插入图片描述
在这里插入图片描述
这里共有42对合约满足条件,进而判断合约对之间的协整性。

协整性

在进行协整性检验之前首先要验证数据是否为一阶单整的,即原始数据非平稳而一阶差分之后变为平稳的。我们以raw_clp列为检验原始数据是否为平稳的结果,diff_clp列为检验一阶差分之后的数据是否为平稳的结果,平稳为1,非平稳为0,显著性水平为0.01。可以看到数据满足一阶单整的条件,进而判断是否为协整的。
在这里插入图片描述
可以看到合约池中的合约都满足一阶单整的条件,可以进行协整性的判定。
最后选出了四对具有协整性(在显著性水平 α = 0.01 \alpha=0.01 α=0.01条件下)的合约,从中选取相关性较大的前三对(121408与43409, 681409和791409, 681409与851410)进行配对交易。
在这里插入图片描述

策略构建

以681409与791409为例,做出二者中心化之后的价差,可以看到价差数据在0附近波动,我们设置一定的阈值,例如正负一个(或者两个标准差 σ σ σ)作为均值回复的区间进而在价差高于给定阈值时即价差值大于σ时做空高价格标的做多低价格标的,而价差值小于 − σ -σ σ时反向操作。在这里插入图片描述
在实际的操作中不同的品种对应该设置不同的阈值。
下面进入策略构建环节strat_pair(id=0, sig=1.8, loss=0.05):
(具体代码有需要可以私信我:))
首先设置参数如下:
id:合约对的id,在这里我们选出了三个(0, 38, 39)
sig:为标准差的倍数,将其乘以标准差后作为上下界阈值,如果当前价差值在上界做空,反之做多。
loss:止损率,如果当前一笔交易的收益为负,并且其绝对值小于总收益的loss倍数,则平仓止损。
分别展示出三对合约的策略的参数以及对应的输出信息(在实际的测试当中当选取sig=1.8,loss=0.03时,发现在三对合约上都有较好的表现,因此本策略具有较强的鲁棒性)

回测结果

Id=0时:strat_pair(id=0, sig=1.8, loss=0.03)
在这里插入图片描述
Id=38时:
在这里插入图片描述
Id=39时:
在这里插入图片描述

总结

此策略在传统的配对交易上加入了止损,同时,在筛选标的对的时候结合了相关性以及协整性,使得筛选出来的标的对更符合配对交易的宗旨,可以看到具有很好的效果。当然有待改进的地方也有几处,例如如何权衡止损率和换手率之间的关系?欢迎小伙伴批评指教!

核心代码示例

def find_pairs(val):
    clp_data = pd.read_csv('min_clp_data.csv')
    clp_data.index = clp_data[['date', 'time']]
    close_data = clp_data.drop(columns=['date', 'time'])
    sns.heatmap(close_data.corr(), annot=True, square=True)
    plt.show()
    cor = close_data.corr()
    A = list()
    B = list()
    C = list()
    for i in range(len(cor))[1:]:
        for j in range(i + 1, len(cor)):
            if cor.iloc[i, j] > val:
                A.append(cor.index[i])
                B.append(cor.index[j])
                C.append(cor.iloc[i, j])
    pairs = pd.DataFrame([A, B, C], index=['cont1', 'cont2', 'cor_val'])
    return pairs, close_data

def check_pairs(pairs, close_data):
    raw = list()
    one_lag = list()
    for i in range(pairs.shape[1]):
        res1 = adfuller(np.diff(close_data[pairs[i].iloc[0]]))
        res2 = adfuller(np.diff(close_data[pairs[i].iloc[1]]))
        if res1[0] < res1[4]['1%'] and res2[0] < res2[4]['1%']:
            one_lag.append('1')
        else:
            one_lag.append('0')
        res1 = adfuller(close_data[pairs[i].iloc[0]])
        res2 = adfuller(close_data[pairs[i].iloc[1]])
        if res1[0] < res1[4]['1%'] and res2[0] < res2[4]['1%']:
            raw.append('1')
        else:
            raw.append('0')
    check = pd.DataFrame([raw, one_lag], index=['raw_clp', 'diff_clp']).T
    coint_res = list()
    for i in range(pairs.shape[1]):
        co_val = coint(close_data[pairs[i].iloc[0]], close_data[pairs[i].iloc[1]])
        if co_val[0] <= co_val[2][2]:
            coint_res.append(1)
        else:
            coint_res.append(0)
    coint_res = pd.DataFrame(coint_res)
    var = coint_res[coint_res[0] == 1].index
    final_pairs = pairs[coint_res[coint_res[0] == 1].index]
    return final_pairs

def strat_pair(id=0, sig=1.8, loss=0.05):
    pos = [0, ]
    ctlist = pd.read_csv('ContractList.csv')
    cs0 = ctlist[ctlist['ContractId'] == int(pairs[id].iloc[0])]['ContractSize'].iloc[0]
    cs1 = ctlist[ctlist['ContractId'] == int(pairs[id].iloc[1])]['ContractSize'].iloc[0]
    clp = close_data[pairs[id][:2]]
    jc = close_data[pairs[id].iloc[1]] - close_data[pairs[id].iloc[0]]
    jc_val = jc - np.mean(jc)
    sigma = np.std(jc_val)
    pro = list()
    pro.append(10000)
    up_val = sig * sigma
    down_val = - up_val
    for i in range(len(jc_val))[1:]:
        if pos[-1] == 0:
            if jc_val[i] >= up_val:  
                ret1 = (clp.iloc[i - 1, 1] - clp.iloc[i, 1]) * cs1
                ret0 = (clp.iloc[i, 0] - clp.iloc[i - 1, 0]) * cs0
                pro.append(ret1 + ret0)
                pos.append(-1)
            elif jc_val[i] <= down_val:  
                ret1 = (clp.iloc[i, 1] - clp.iloc[i - 1, 1]) * cs1
                ret0 = (clp.iloc[i - 1, 0] - clp.iloc[i, 0]) * cs0
                pro.append(ret1 + ret0)
                pos.append(1)
            else:
                pro.append(0)
                pos.append(0)
        else:
            if pos[-1] == -1: 
                if jc_val[i] > down_val:  
                    ret1 = (clp.iloc[i - 1, 1] - clp.iloc[i, 1]) * cs1
                    ret0 = (clp.iloc[i, 0] - clp.iloc[i - 1, 0]) * cs0
                    if ret1 + ret0 < 0 and abs(ret1 + ret0) < sum(pro) * loss:
                        pro.append(0)
                        pos.append(0)
                        continue
                    pro.append(ret1 + ret0)
                    pos.append(-1)
                else:  
                    ret1 = (clp.iloc[i, 1] - clp.iloc[i - 1, 1]) * cs1
                    ret0 = (clp.iloc[i - 1, 0] - clp.iloc[i, 0]) * cs0
                    if ret1 + ret0 < 0 and abs(ret1 + ret0) < sum(pro) * loss:
                        pro.append(0)
                        pos.append(0)
                        continue
                    pro.append(ret1 + ret0)
                    pos.append(1)
            else:  
                if jc_val[i] < up_val: 
                    ret1 = (clp.iloc[i, 1] - clp.iloc[i - 1, 1]) * cs1
                    ret0 = (clp.iloc[i - 1, 0] - clp.iloc[i, 0]) * cs0
                    if ret1 + ret0 < 0 and abs(ret1 + ret0) < sum(pro) * loss:
                        pro.append(0)
                        pos.append(0)
                        continue
                    pro.append(ret1 + ret0)
                    pos.append(1)
                else:  
                    ret1 = (clp.iloc[i - 1, 1] - clp.iloc[i, 1]) * cs1
                    ret0 = (clp.iloc[i, 0] - clp.iloc[i - 1, 0]) * cs0
                    if ret1 + ret0 < 0 and abs(ret1 + ret0) < sum(pro) * loss:
                        pro.append(0)
                        pos.append(0)
                        continue
                    pro.append(ret1 + ret0)
                    pos.append(-1)
    profit = np.cumsum(pro)
    fig = plt.figure()
    ax = plt.subplot(211)
    ax.plot(profit)
    ax.set_ylabel('profit')
    ax = plt.subplot(212)
    ax.plot(pos)
    ax.set_ylabel('position')
    ax.set_xlabel('t')
    p = (pairs[id].iloc[0], pairs[id].iloc[1])
    return pos, profit, p

代码以及数据链接: https://pan.baidu.com/s/1oRdBkdg9hqf9AppreUFFeA 提取码: i19x

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤单又灿烂的Quant

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值