套利在金融市场中被广泛应用,其本质是通过利用不同市场、品种或时间段的价格差异来获取利润。一般来说,根据开仓腿数的不同,套利可以分为一条腿、两条腿、三条腿和四条腿的套利。
一条腿的套利(期现套利)
期现套利是一种简单的套利方式,其本质是利用期货市场和现货市场之间的价格差异进行套利,是跨期套利的一种特例。具体参考策略实现,可以查看文章基于生猪品种的基差套利策略。
两条腿的套利
两条腿的套利包括跨期套利、跨品种套利和跨市场套利。这是绝大多数交易者采取的套利方式,其中跨期套利是通过不同交割日期的合约之间的价差进行套利,跨品种套利则是利用不同品种合约之间的价格差异进行套利。具体参考策略实现,可以查看文章传统模型跨期套利策略(传统商品期货)。
三条腿的套利(蝶式套利)
蝶式套利利用中间月份合约的凸性和凹型特征进行套利。经典的蝶式套利包括甲醇的91反套和15正套构成的套利。这种套利方式通过构造三个不同合约的组合,利用其价差变化来获取利润。具体参考策略实现,可以查看文章浅谈商品期货中的蝶式套利策略。
四条腿的套利(矩阵套利)
矩阵套利是一种相对复杂的套利策略,利用两个套利合约之间的对冲来获取利润,因此被称为套利的套利。与传统的套利方式不同,矩阵套利同时考虑了时间和标的两个维度,从而更加灵活地捕捉市场波动。
在本文中,我们将着重介绍四条腿的套利策略——矩阵套利。矩阵套利以回归思想为基础,旨在使两个相关品种(比如为上下游产业链的玻璃和纯碱,同属于MTO装置的产物PP和PE等)之间的价差结构趋向一致,要么都呈现正向市场结构,要么都呈现反向市场结构。其核心目标在于捕捉价差结构从不合理到合理的转变过程中产生的收益。具体而言,矩阵套利的策略是基于远期价差结构的回归趋势,同时通过对近期价差进行对冲,以实现风险收益的平衡。因此,从风险收益的角度来看,矩阵套利具有相对稳健性,能够在市场波动中获取利润并控制风险。
具体来说,矩阵套利的基本原理是构造一个套利组合,通过这个组合的价格变化来获取利润。假设我们有两个品种 A 和 B,分别对应不同的期货合约,我们可以构造一个简单的矩阵,用正号表示做多的品种,用负号表示做空的品种,从而构造一个套利组合价差,具体可以分为两种情况:
情形(1):合约A升水,合约B贴水
合约A现在呈现的是正向市场结构,近期合约的价格低于远期合约价格;合约B现在呈现的是反向市场结构,近期合约的价格高于远期合约的价格。那么按照回归的思维方式,我们可以构造如下矩阵:
情形(2):合约A贴水,合约B升水
于此相反情况下,合约A现在呈现的是负向市场结构,近期合约的价格高于远期合约价格;合约B呈现正向市场结构,近期合约低于远期合约。构造如下矩阵:
因此矩阵套利是一种相对较复杂的套利策略,其原理是同时考虑时间和标的两个维度,通过构造多个套利组合来获取利润。具体而言,矩阵套利利用不同合约、不同品种之间的价格关系,构建套利组合,从而实现对冲风险、获取稳定收益的目的。
矩阵套利的核心思想是构建一个套利组合,利用不同合约之间的价差变化来获取利润。其具体步骤如下:
构建套利组合:选择两个合约作为套利组合,获取相关合约的近远期价格。
计算近远期价差:计算两个不同合约的近远期价差。
价差结构失衡进行买卖操作:判断价差分布结构失衡,即同时出现一个升水和贴水结构,对不同合约进行买入或卖出操作。
利用价差回归获取利润:判断价差结构重归为平衡(同为升水或者贴水),平仓了结。
接下来,我们将介绍如何实现矩阵套利策略的代码逻辑。以下是一个使用Python编写的简单示例代码:
# 构建目标合约列表
commodity_contracts = {
"SA": ['01', '05', '09'], # 纯碱
"FG": ['01', '05', '09'], # 玻璃
}
# 获取主力/次主力合约代码
def getTarList():
mainList = [] # 主力合约列表
nextList = [] # 次主力合约列表
for commodity, monthList in commodity_contracts.items():
curTime = _D()
curYear = curTime[2:4]
curMonth = curTime[5:7]
if int(curMonth) == 12:
if commodity.isupper():
mainID = commodity + str(int(curYear[1])+1) + str(monthList[1])
nextID = commodity + str(int(curYear[1])+1) + str(monthList[2])
else:
mainID = commodity + str(int(curYear)+1) + str(monthList[1])
nextID = commodity + str(int(curYear)+1) + str(monthList[2])
elif int(curMonth) >= int(monthList[0]) and int(curMonth) < int(monthList[1]) - 1:
if commodity.isupper():
mainID = commodity + str(curYear[1]) + str(monthList[1])
nextID = commodity + str(curYear[1]) + str(monthList[2])
else:
mainID = commodity + str(curYear) + str(monthList[1])
nextID = commodity + str(curYear) + str(monthList[2])
elif int(curMonth) >= int(monthList[1]) - 1 and int(curMonth) < int(monthList[2]) - 1:
if commodity.isupper():
mainID = commodity + str(curYear[1]) + str(monthList[2])
nextID = commodity + str(int(curYear[1])+1) + str(monthList[0])
else:
mainID = commodity + str(curYear) + str(monthList[2])
nextID = commodity + str(int(curYear)+1) + str(monthList[0])
elif int(curMonth) < 12:
if commodity.isupper():
mainID = commodity + str(int(curYear[1])+1) + str(monthList[0])
nextID = commodity + str(int(curYear[1])+1) + str(monthList[1])
else:
mainID = commodity + str(int(curYear)+1) + str(monthList[0])
nextID = commodity + str(int(curYear)+1) + str(monthList[1])
mainList.append(mainID)
nextList.append(nextID)
return [mainList, nextList]
def getPrice(contract):
exchange.SetContractType(contract)
r = exchange.GetRecords(PERIOD_D1)
if len(r) < 2:
return
return r[-2].Close
def main():
p = ext.NewPositionManager()
posInfo = []
while True:
lNearID, lFarID, ppNearID, ppFarID = getTarList()[0][0], getTarList()[1][0], getTarList()[0][1], getTarList()[1][1]
lNearPrice, lFarPrice, ppNearPrice, ppFarPrice = getPrice(lNearID), getPrice(lFarID), getPrice(ppNearID), getPrice(ppFarID)
if posInfo is None or lNearPrice is None or lFarPrice is None or ppNearPrice is None or ppFarPrice is None:
return
posInfo = exchange.GetPosition()
if not posInfo and lNearPrice > lFarPrice and ppNearPrice < ppFarPrice:
Log('合约A贴水:', lNearID, lNearPrice, lFarID, lFarPrice, lNearPrice - lFarPrice)
Log('合约B升水:', ppNearID, ppNearPrice, ppFarID, ppFarPrice, ppNearPrice - ppFarPrice)
p.OpenLong(lFarID,10)
p.OpenShort(lNearID,10)
p.OpenShort(ppFarID,10)
p.OpenLong(ppNearID,10)
if not posInfo and lNearPrice < lFarPrice and ppNearPrice > ppFarPrice:
Log('合约A升水:', lNearID, lNearPrice, lFarID, lFarPrice, lNearPrice - lFarPrice)
Log('合约B贴水:', ppNearID, ppNearPrice, ppFarID, ppFarPrice, ppNearPrice - ppFarPrice)
p.OpenLong(lNearID,10)
p.OpenShort(lFarID,10)
p.OpenShort(ppNearID,10)
p.OpenLong(ppFarID,10)
if posInfo and ((lNearPrice < lFarPrice and ppNearPrice < ppFarPrice) or (lNearPrice > lFarPrice and ppNearPrice > ppFarPrice)):
Log('回归正常结构')
p.CoverAll()
Sleep(1000 * 60 * 60 * 24)
构建目标合约列表
在代码中,首先定义了一个字典 commodity_contracts
,包含待交易品种的主力合约月份。然后使用getTarList
函数,获取合约A和合约B的主力和次主力具体合约代码。
获取价格数据
获取套利品种的价格数据。
套利策略判断
根据获取的价格数据分析市场结构,确定两个不同品种套利方向(如贴水或升水);当两合约呈现不同的价差结构时,表示出现价差结构失衡。
执行入场开仓策略
根据两合约价差结构,执行近月和远月合约的开仓和平仓操作,即根据不同合约贴水或升水情况执行买入和卖出操作。
执行平仓操作
判断持有仓位情况下,当价差结构重归为平衡,平仓离场。
使用玻璃和纯碱两个相关品种,在2024年1月至4月,根据回测结果显示,策略获取了较为稳定的收益。
矩阵套利作为一种复杂的套利策略,结合了跨期套利和跨品种套利的特点,能够更加灵活地捕捉市场波动,提高收益。通过对市场结构进行分析,构建合适的套利组合,并根据价差变化进行交易,可以实现稳健的收益增长。大家可以根据自己自己关注的品种进行尝试。
课程参考链接:
转载自:优宽量化交易平台社区
作者:ianzeng123