商品期货套利策略(附代码)

引言

商品期货套利是期货套利交易的一种类型,其基本原理在于当市场价格关系处于不正常状态时进行双边交易以获取低风险差价。本文将结合海通证券发布于 2016 年 8 月 3 日的研究报告《绝对收益策略系列研究之二——商品期货套利策略》向大家简单介绍这一策略,并在量子金服投研管理平台上加以实现。

概念简介

商品期货套利主要有四种模式:

1)期现套利

期现套利是指利用同一种商品,在期货市场与现货市场之间存在不合理的价差进行套利的行为。商品期货价格与现货价格之间的差值被称为“基差”,基差的值在一段时间内都应当是稳定的。当基差出现不合理的偏差时,套利者即可通过构建现货与期货的套利组合,以期望基差在未来回归合理的价值区间从而获取利润。

2)跨期套利

跨期套利是指利用同一商品、两个不同结算时间的合约,在市场上同时买进、卖出,通过价差的扩大和缩小来获取利润。跨期套利策略包括基本面套利和统计套利。基本面套利需要实际考察商品的供需状态、消费库存比等等信息。而统计套利是利用不同期限合约直接的协整关系,建立一个有均值回复特征的多空组合。

3)跨品种套利

跨品种套利是指利用两种不同的,但是有很强相关性的商品之间的合约进行套利交易。通过买卖相同时间的不同商品合约,以期在未来二者价差扩大或缩小,再对冲平仓获利。跨品种套利主要包括产业链条例和替代品种套利。产业链套利指的是在同一条产业链中选择两个或多个品种。这些品种往往相关性较高,无论从基本面还是统计结果出发都比较容易找到逻辑支撑,比较常见的产业链如炼钢、大豆压榨。而替代品种套利指的是选取功能上存在较强替代性的两种产品,其价格往往能够反映出一定的相关性,例如豆油和棕榈油。

4)跨市场交易

跨市场交易是指在不用市场之间对同一期货合约进行套利交易。由于区域地理等差别因素,各商品合约存在一定的稳定价差关系。当这一价差由于不同市场的供求关系、市场环境甚至交易规则等方面的因素产生偏离时,套利者即可通过在不同市场买卖同一商品合约的行为获利。

在以上四种套利模式中,期现套利要求投资者参与现货市场交易,而跨市场交易则要求投资者在不同市场、主要是不同国家的市场进行交易,在操作上存在一定的不便,我们在本文中不再多加讨论。针对跨期套利中的统计套利,与跨品种套利中的替代品套利,下面我们将制定简单的交易策略。

策略思路

商品套利的策略逻辑基本一致,都在于判断时点,买入卖出同时进行,赚取差价。不同套利策略主要的差异在于投资标的选择的不同,以及时点判断方式的不同。具体的步骤为:
1)选择合约标的,一般数量为两个。记为合约 A,B,两者满足较强的相关关系;
2)制定 A 相对于 B 的相对价格指标,记为 S;
3)当 S 大于开仓阈值时,开多 A、开空 B;当 S 小于平仓阈值或大于止损阈值时时,平仓;反之亦然;

策略细节

1)标的选取

对于跨期套利策略,我们选取流动性良好,且统计检验满足协整关系的橡胶期货合约作为投资标的。橡胶期货主力合约在 1,5,9 三个月切换,且主力合约在交割前一个月的月初完成。因此我们在每年的 12、1、2、3 月交易 5 月和 9 月的合约,4、5、6、7 月交易 9 月和 1 月合约,8、9、10、11 月交易 1 月和 5 月合约,在 3、7、11 月的最后一个交易日收盘时平仓,下个交易日起更换套利合约。

对于跨品种套利策略,我们选取替代性较强,相关性较好的豆油、棕榈油作为标的。选取每天的主力合约进行交易。

相对价格指标

经过回归得到协整系数近似为 1,因此取头寸比例固定为 1:1

对价差进行标准化处理:

其中为过去 N 个价差的平均值,为价差的标准差。基于均值回复的特征,当价差大于开仓阈值时建仓,回复至平仓阈值或是突破止损阈值时平仓,具体参数如下:

2)交易手数

每次双边各交易相同手数

3)保证金(MARGIN)

设为 50%,即杠杆率为 2

4)回溯期(N)

可自行定义

5)开仓阈值(ENTRYSCORE)

2 倍标准差

6)平仓阈值(EXITSCORE)

0.5 倍标准差

7) 止损阈值(STOPSCORE)

3 倍标准差

在实现过程中,参数设置:
WINDOW=100
ENTRY_SCORE=2
EXIT_SCORE=0.5
STOP_LOSS_SCORE=3
MARGIN=0.5

策略实现
1)跨品种套利
交易标的:豆油(Y)、棕榈油(P)主力合约
回测时间:2012.01.01~2017.01.01
回测时长:5 年

收益曲线
1e9a89af034a42bdb45750943a576b92-image.png

收益归因
431534065c7b46c99a4418e1e30c2957-image.png

业绩分析
ecffbe253a4c4a5785e0ed1b5c150da5-image.png

如图,我们的收益很一般,为了分析原因,我们对相对价格指标和它标准化后的结果作图分析:
bddbd2a0e44d48509a800306a50e6af1-image.png

如图,红色线为相对价格指标,蓝色线为标准化后的指标。从图中可以发现,在回测期间内(2012.01.01~2017.01.01)价格指标呈现较大幅度的波动,但总体依然表现出了均值复归的特质。且标准化后的指标对与原价差变动相对吻合,应当有一定程度的预测作用。收益率过低可能是因为我们使用了 50%的保证金,杠杆率过低。
38e2b020353d41788501fdcd4cad399f-image.png

如图,我们的策略在最多的时候也只使用了不到 35%的保证金,持有过多现金可能是导致我们收益率平庸的原因。

下调保住金率后:
d13b53415e8847b59e78227ec80a1c40-image.png
2d70ab4da776443e92921086ed6ee042-image.png
dc10b01d1e43426993c1caf2895eeb01-image.png
d2277d66ea514733bb60f18f339af5ef-image.png

如图所示,收益率显著提升。当然,更高的杠杆率也带来更大的风险,我们的回撤与波动率都上升较大。不过策略的夏普率也有较大提升,可以说总体是表现更好的。

2)跨期套利
交易标的:天然橡胶主力合约
回测时间:2015.01.01~2016.05.01
回测时长:16 个月

收益曲线
e3853b470b614123b68f7cdffe6e937c-image.png

收益归因
30a2dd8be7834f19807d8427ead505a1-image.png

业绩分析
e74c41b86f4847209a7222a270964864-image.png

可以发现,在最大回测 52.9%的情况下,该天然橡胶期货的跨期套利策略实现年化收益率 19.4%,总收益率 25.07%。策略中的回溯期 N 为 50,保证金比率统一设置为 13%。每次开仓均利用可用余额的 80%进行操作。由于杠杆率较高,实现了不错的收益率。

另一方面,发现在 15 年 4 月与 16 年 1 月之间交易较少,猜想阈值设置偏高,导致交易触发事件减少。实际操作时可根据自身需要进行参数设置。

小结
本文对商品期货的跨品种套利和跨期套利策略进行了简单的介绍。通过量子金服投研平台我们实现了这两种策略并进行分析。需要指出的是,我们采用的是日线操作,而商品期货套利的机会稍纵即逝,在实际操作中采用分钟线为好,这里我们仅做介绍,读者可通过量子金服投研平台自行编写策略进行实现。作为一种比较成熟的期货套利方式,商品期货的跨期和跨品种套利策略在回测过程中实现了较好的收益率,但该策略在实际使用过程中的诸多参数设置,读者需要根据实际情况进行调整,切不可一成不变。

关于回测平台、历史数据,可以在这里下载:http://www.yunkuanke.com/#/introduce
这个平台的数据都是经过清洗的,而且策略不会外露,有保密系统,还是比较安全的。
想学习更多量化策略,也可以来这里看看:http://www.yunkuanke.com/#/lzClass
想学习更多关于量化投资策略相关内容的,可以关注微信:量化投资与金融科技(微信号:QuantumFintech)
微信二维码:

Code:
1)跨品种套利

# -*- coding:utf-8 -*-

from CloudQuant import SDKCoreEngine  # 导入量子金服SDK
from CloudQuant import AssetType
from CloudQuant import QuoteCycle
from CloudQuant import OrderType
import numpy as np  # 使用numpy
import pandas as pd
import math
import matplotlib.pyplot as plt

np.seterr(invalid='ignore')

COMMODITY=['Y.DCE','P.DCE']
WINDOW=100
ENTRY_SCORE=2
EXIT_SCORE=0.5
STOP_LOSS_SCORE=3
MARGIN=0.1

global score_stored
score_stored=[]
global delta
delta=[]

config = {
'username': 'pengkun',
'password': '111111',
'rootpath': 'c:/cStrategy',  # 客户端所在路径
'assetType': AssetType.Future,
'initCapitalFuture': 10000000,  # 初始资金
'startDate': 20120101,  # 交易开始日期
'endDate': 20170101,  # 交易结束日期
'cycle': QuoteCycle.D,  # 回放粒度为1分钟线
'feeRate': 0.001,
'feeLimit': 5,
'strategyName': 'commodity_arbitrage',  # 策略名
"logfile": "ma.log",
 'dealByVolume': True
 }

def dateAdd(year_month,add):
year=int(year_month[0:2])
month=int(year_month[2:4])
month=month+add
if month>12:
year+=1
month-=12
return str(year*100+month)

def initial(sdk):
flag = 0.0  # 标记状态
sdk.setGlobal('flag', flag)

def initPerDay(sdk):

#获取当前主力合约
date=sdk.getNowDate()
year_month=str(date)[2:6]
code=sdk.getFactorData("LZ_CN_CF_CODE")
main_contract=sdk.getFactorData("LZ_CN_CF_DOMINANT_CONTRACT")[-1]
main_contract=pd.Series(main_contract,index=code)
contract=[]
for c in COMMODITY:
temp = 0
while(temp!=1):
    temp_c=c[0]+year_month+c[1:]
    if temp_c in main_contract:
        temp=main_contract[temp_c]
    year_month=dateAdd(year_month,1)
contract.append(temp_c)
sdk.setGlobal("c_a",contract[0][0:5])
sdk.setGlobal("c_b",contract[1][0:5])

def strategy(sdk):
flag=sdk.getGlobal('flag')
contract_a=COMMODITY[0]
contract_b=COMMODITY[1]
contract=[contract_a,contract_b]
print contract
hist=sdk.getLatest(contract,WINDOW,"D",2)
if len(hist[contract_a])>=WINDOW and len(hist[contract_b])>=WINDOW:
hist_a=[0.0]*WINDOW
hist_b=[0.0]*WINDOW
for i in range(WINDOW):
    hist_a[i]=math.log(hist[contract_a][i].close)
    hist_b[i]=math.log(hist[contract_b][i].close)
hist_a=np.array(hist_a)
hist_b=np.array(hist_b)
# spread=hist_b-hist_a
spread=hist_a-hist_b
mean=np.nanmean(spread)
std=np.std(spread)
score=(spread[-1]-mean)/std

contract_a = sdk.getGlobal("c_a")
contract_b = sdk.getGlobal("c_b")
contract = [contract_a, contract_b]
print contract
q=sdk.getQuotes(contract,2)
price_a=q[contract_a].open
price_b=q[contract_b].open

margin_a=MARGIN
margin_b=MARGIN

multiple_a=10
multiple_b=10

volume_a= (sdk.getAccountInfo(tsInd=2).availableCash) //(price_a*margin_a*multiple_a+price_b*margin_b*multiple_b)
volume_a=int(volume_a)
volume_b=volume_a

pos_a=0
pos_b=0
#清仓
pos=sdk.getPositions(2)
for f in pos:
    if f.code==contract_a:
        pos_a=f.optPosition
    elif f.code==contract_b:
        pos_b=f.optPosition
    else:
        ordertype=f.orderType
        quote=sdk.getQuote(f.code,tsInd=2)
        if quote:
            price=quote.open

            sdk.makeOrder(f.code,price,f.optPosition,-ordertype,tsInd=2)

if (score<EXIT_SCORE and flag > EXIT_SCORE) or (score>STOP_LOSS_SCORE and flag<STOP_LOSS_SCORE):  # 下穿止盈点或上穿止损点,平仓
    sdk.makeOrder(contract_a, price_a, pos_a, -1, tsInd=2)
    sdk.makeOrder(contract_b, price_b, pos_b, -2, tsInd=2)
    sdk.sdklog("------------------------------------------")
    sdk.sdklog(sdk.getNowDate(), "DATE")
    sdk.sdklog([contract_a, price_a, pos_a, -1], 'CLOSE_LONG')
    sdk.sdklog([contract_b, price_b, pos_b, -2], 'CLOSE_SHORT')

if (score>-EXIT_SCORE and flag<-EXIT_SCORE) or (score<-STOP_LOSS_SCORE and flag>-STOP_LOSS_SCORE):     #上穿止盈点,平仓
    sdk.makeOrder(contract_a, price_a, pos_a, -2, tsInd=2)
    sdk.makeOrder(contract_b, price_b, pos_b, -1, tsInd=2)
    sdk.sdklog("------------------------------------------")
    sdk.sdklog(sdk.getNowDate(), "DATE")
    sdk.sdklog([contract_a, price_a, pos_a, -2], 'CLOSE_SHORT')
    sdk.sdklog([contract_b, price_b, pos_b, -1], 'CLOSE_LONG')

if score<-ENTRY_SCORE and flag>-ENTRY_SCORE:     #下穿下界,买入
    sdk.makeOrder(contract_a, price_a, volume_a, 2, tsInd=2)
    sdk.makeOrder(contract_b, price_b, volume_b, 1, tsInd=2)
    sdk.sdklog("------------------------------------------")
    sdk.sdklog(sdk.getNowDate(), "DATE")
    sdk.sdklog([contract_a, price_a, volume_a, 2], 'OPEN_SHORT')
    sdk.sdklog([contract_b, price_b, volume_b, 1], 'OPEN_LONG')

if score>ENTRY_SCORE and flag<ENTRY_SCORE:     #上穿上界,卖出
    sdk.makeOrder(contract_a, price_a, volume_a, 1, tsInd=2)
    sdk.makeOrder(contract_b, price_b, volume_b, 2, tsInd=2)
    sdk.sdklog("------------------------------------------")
    sdk.sdklog(sdk.getNowDate(), "DATE")
    sdk.sdklog([contract_a, price_a, volume_a, 1], 'OPEN_LONG')
    sdk.sdklog([contract_b, price_b, volume_b, 2], 'OPEN_SHORT')

if score>STOP_LOSS_SCORE:
    flag=STOP_LOSS_SCORE+1
elif score>ENTRY_SCORE:
    flag=(STOP_LOSS_SCORE+ENTRY_SCORE)/2
elif score>EXIT_SCORE:
    flag=(ENTRY_SCORE+EXIT_SCORE)/2
elif score>-EXIT_SCORE:
    flag=0
elif score>-ENTRY_SCORE:
    flag=(-ENTRY_SCORE-EXIT_SCORE)/2
elif score>-STOP_LOSS_SCORE:
    flag=(-STOP_LOSS_SCORE-ENTRY_SCORE)/2
else:
    flag=-STOP_LOSS_SCORE-1

print flag
score_stored.append(score)
delta.append(spread[-1])
sdk.setGlobal("flag",flag)

def main(): 
# 将策略函数加入
config['initial'] = initial
config['strategy'] = strategy
config['preparePerDay'] = initPerDay
# 启动SDK
SDKCoreEngine(**config).run()
fig=plt.figure()
# plt.plot(score_stored)
# plt.plot(delta)
ax1 = fig.add_subplot(111)
ax1.plot(score_stored,label='score_standard')
ax1.plot([2]*len(score_stored),'--')
ax1.plot([-2] * len(score_stored), '--')
ax1.plot([3] * len(score_stored), '--')
ax1.plot([-3] * len(score_stored), '--')
ax1.plot([0] * len(score_stored), '--')
ax1.plot([0.5] * len(score_stored), '--')
ax1.plot([-0.5] * len(score_stored), '--')
ax2 = ax1.twinx()  # this is the important function
ax2.plot(delta,'r',label="score")

plt.show()

if __name__ == "__main__":
main()

2)天然橡胶跨期套利(节选)

 # -*- coding=UTF-8 -*-

from CloudQuant import SDKCoreEngine  # 导入量子金服SDK
from CloudQuant import AssetType
from CloudQuant import QuoteCycle
import numpy as np
import pandas as pd
import matplotlib as ml

config = {
'username': 'maodaren',  # 访问SDK所使用的用户名,即云宽客的登录名
'password': '111111',  # 访问SDK所使用的用户名对应的密码
'rootpath': 'd:/cStrategy/',  # 基础数据存放的路径,即客户端所在路径
'initCapitalFuture': 10000000,  # 期货初始资金 1千万
'startDate': 20150101,  # 回测开始时的日期
'endDate': 20160501,  # 回测结束时的日期
'cycle': QuoteCycle.D,  # 进行回测前的时间粒度,int类型,定义在枚举QuoteCycle中
'assetType': AssetType.Future,
'feeRate': 0.001,  # 手续费率
'feeLimit': 5,  # 最低手续费
'strategyName': '跨品种套利RU',  # 策略名
'dealByVolume': True  # 撮合是否考虑实际交易量
 }
flag = []
def initial(sdk):
count = -1
days = 20
sdk.setGlobal('DAYS', days)
sdk.setGlobal('COUNT', count)

 def initPerDay(sdk):
COUNT = sdk.getGlobal('COUNT')
COUNT += 1
sdk.setGlobal('COUNT', COUNT)

 def strategy(sdk):
# 获取全局变量
 deal_cal = np.array(sdk.getFactorData('LZ_CN_CF_EXCH_CAL'))       # 商品期货交易日历
futureClose = np.array(sdk.getFactorData("LZ_CN_CF_QUOTE_CLOSE"))        # 获取商品期货收盘价因子
futureOI = np.array(sdk.getFactorData("LZ_CN_CF_QUOTE_OI"))      # 获取商品期货持仓量因子
futureList = np.array(sdk.getFactorData("LZ_CN_CF_CODE"))        # 商品期货合约代码因子
heyuechengshu = np.array(sdk.getFactorData('LZ_CN_CF_MULTIPLIER'))

COUNT = sdk.getGlobal('COUNT')
print '第',COUNT+1,'天'
 now_date = sdk.getNowDate()
# 当前月份
print now_date
now_day = now_date%100
now_month = now_date/100%100
now_year = now_date/10000%100
now_date_fresh = now_month+now_year*100

 deal_month_1 = [12,1,2,3]
 deal_month_2 = [4,5,6,7]
deal_month_3 = [8,9,10,11]

varietyname = ['RU']
futurecodes = {}
futurecodes_1 = {}

for i in range(len(varietyname)):
futureCode = []
futureIflist = []
futureIflistSite = []
futurecodes[varietyname[i]] = []
futurecodes_1 = {}

for j in range(len(futureList)):
    futurecode = futureList[j][:-4]
    futurecodes_1[futurecode]=[]
    if varietyname[i].lower() == futurecode[:len(varietyname[i])].lower():
        futureIflist.append(futurecode)
        futureIflistSite.append(j)
        futurecodes[varietyname[i]].append(futurecode)
        futurecodes_1[futurecode].append(j)
if futureIflist is None:
    print '该类期货目前没有合约'
    break
#选择出今年的主力合约 1,5,9月份
main_future = {}
main_future['thisyear']={}
main_future['thisyear']['1']=[]
main_future['thisyear']['5']=[]
main_future['thisyear']['9']=[]
main_future['nextyear']={}
main_future['nextyear']['1'] = []
main_future['nextyear']['5'] = []
main_future['nextyear']['9'] = []
for num in range(len(futurecodes['RU'])):
    temp = (int(futurecodes['RU'][num][2]) != 0)
    if temp:
        if int(futurecodes['RU'][num][2:])/100 == now_year:
            if int(futurecodes['RU'][num][2:])%100 == 1:
                main_future['thisyear']['1'].append(futurecodes['RU'][num])
            elif int(futurecodes['RU'][num][2:])%100 == 5:
                main_future['thisyear']['5'].append(futurecodes['RU'][num])
            elif int(futurecodes['RU'][num][2:])%100 == 9:
                main_future['thisyear']['9'].append(futurecodes['RU'][num])
            else:
                pass
        elif int(futurecodes['RU'][num][2:])/100 == now_year+1:
            if int(futurecodes['RU'][num][2:])%100 == 1:
                main_future['nextyear']['1'].append(futurecodes['RU'][num])
            elif int(futurecodes['RU'][num][2:])%100 == 5:
                main_future['nextyear']['5'].append(futurecodes['RU'][num])
            elif int(futurecodes['RU'][num][2:])%100 == 9:
                main_future['nextyear']['9'].append(futurecodes['RU'][num])
            else:
                pass

 if now_month in deal_month_1:    #当前月份为[12,1,2,3],交易5月和9月合约。
if now_month is not 12:

    position1 = futurecodes_1[main_future['thisyear']['5'][0]]
    position2 = futurecodes_1[main_future['thisyear']['9'][0]]
    heyuecs = heyuechengshu[position1]
    price_serials_1 = futureClose[-50:, position1]
    price_serials_2 = futureClose[-50:, position2]
    cut = np.array(np.log(price_serials_1) - np.log(price_serials_2))
    cut_std = np.std(cut)
    avg = np.nanmean(cut)
    st = (cut[-1] - avg) / cut_std
    # 判断flag值
    if 0 < st < 0.5:
        flag.append(0.5)
    elif -0.5 < st < 0:
        flag.append(-0.5)
    elif 3 > st > 2:
        flag.append(2)
    elif -3 < st < -2:
        flag.append(-2)
    elif st > 3:
        flag.append(3)
    elif st < -3:
        flag.append(-3)
    else:
        flag.append(0)
    print '今日flag:%f'%flag[-1]

    # 交易今年主力合约 即买入5月合约卖出9月合约
    if  abs(flag[-1]) == 0.5 :  #平仓操作开始触发
        #获取持仓列表
        print '平仓操作开始触发'
        pos = sdk.getPositions(tsInd = AssetType.Future)
        if pos is None:
            print '取不到持仓行情信息,退出'
            return
        if pos:
            for i in pos:
                quote = sdk.getQuote(i.code,tsInd=AssetType.Future)
                if i.orderType == 1:
                    sdk.makeOrder(i.code,quote.close,i.optPosition,-1,2)
                if i.orderType == 2:
                    sdk.makeOrder(i.code, quote.close, i.optPosition, -2, 2)
                if i.orderType == -1:
                    sdk.makeOrder(i.code,quote.close,i.optPosition,1,2)
                if i.orderType == -2:
                    sdk.makeOrder(i.code,quote.close,i.optPosition,2,2)

    if COUNT > 0 and flag[-1] ==2 and flag[-2] != 2:  #开仓操作开始触发
        print 'flag = 2,开仓操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,1,2)
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,-1,2)

    if COUNT > 0 and flag[-1] ==-2 and flag[-2] != -2:  #开仓操作开始触发
        print 'flag = -2,开仓操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        print volume
        print main_future['thisyear']['5'][0],quote.close,volume
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,-1,2)
        print 1
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,1,2)

    if COUNT > 0 and flag[-1] ==3 and flag[-2] != 3:  #止损操作开始触发
        print 'flag = 3,止损操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,1,2)
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,-1,2)

    if COUNT > 0 and flag[-1] ==-3 and flag[-2] != -3:  #止损操作开始触发
        print 'flag = -3,止损操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,-1,2)
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,1,2)
else:
    position1 = futurecodes_1[main_future['thisyear']['5'][0]]
    position2 = futurecodes_1[main_future['thisyear']['9'][0]]

    heyuecs = heyuechengshu[position1]
    print heyuecs

    price_serials_1 = futureClose[-50:, position1]
    price_serials_2 = futureClose[-50:, position2]
    cut = np.array(np.log(price_serials_1) - np.log(price_serials_2))
    cut_std = np.std(cut)
    avg = np.nanmean(cut)
    st = (cut[-1] - avg) / cut_std
    # 判断flag值
    if 0 < st < 0.5:
        flag.append(0.5)
    elif -0.5 < st < 0:
        flag.append(-0.5)
    elif 3 > st > 2:
        flag.append(2)
    elif -3 < st < -2:
        flag.append(-2)
    elif st > 3:
        flag.append(3)
    elif st < -3:
        flag.append(-3)
    else:
        flag.append(0)
    print '今日flag:%d'%flag[-1] 

    # 交易今年主力合约 即买入5月合约卖出9月合约
    if  abs(flag[-1]) == 0.5 :  #平仓操作开始触发
        #获取持仓列表
        print '平仓操作开始触发'
        pos = sdk.getPositions(tsInd = AssetType.Future)
        if pos is None:
            print '取不到持仓行情信息,退出'
            return
        if pos:
            for i in pos:
                quote = sdk.getQuote(i.code,tsInd=AssetType.Future)
                if i.orderType == 1:
                    sdk.makeOrder(i.code,quote.close,i.optPosition,-1,2)
                if i.orderType == 2:
                    sdk.makeOrder(i.code, quote.close, i.optPosition, -2, 2)
                if i.orderType == -1:
                    sdk.makeOrder(i.code,quote.close,i.optPosition,1,2)
                if i.orderType == -2:
                    sdk.makeOrder(i.code,quote.close,i.optPosition,2,2)

    if COUNT > 0 and flag[-1] ==2 and flag[-2] != 2:  #开仓操作开始触发
        print 'flag = 2,开仓操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,1,2)
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,-1,2)

    if COUNT > 0 and flag[-1] ==-2 and flag[-2] != -2:  #开仓操作开始触发
        print 'flag = -2,开仓操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        print volume
        print main_future['thisyear']['5'][0],quote.close,volume
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,-1,2)
        print 1
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,1,2)

    if COUNT > 0 and flag[-1] ==3 and flag[-2] != 3:  #止损操作开始触发
        print 'flag = 3,止损操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,1,2)
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,-1,2)

    if COUNT > 0 and flag[-1] ==-3 and flag[-2] != -3:  #止损操作开始触发
        print 'flag = -3,止损操作开始触发'
        account = sdk.getAccountInfo(AssetType.Future)  # 获取账户信息
        budget = account.availableCash  # 用可用余额进行建仓操作
        if account is None:
            return
        print 'Account\'s available Cash', budget
        quote = sdk.getQuote(main_future['thisyear']['5'][0],tsInd= AssetType.Future)
        quote_1 = sdk.getQuote(main_future['thisyear']['9'][0],tsInd= AssetType.Future)
        if quote is None or quote_1 is None:
            print '取不到合约行情,退出'
            return
        volume =0.8*budget//(0.13*(quote_1.close+quote.close)*int(heyuecs[0][-1][:2]))
        sdk.makeOrder(main_future['thisyear']['5'][0],quote.close,volume,-1,2)
        sdk.makeOrder(main_future['thisyear']['9'][0],quote_1.close,volume,1,2)

if now_month == 3 and  25<=now_day<=30:  #清仓
 print '月末平仓'
 pos = sdk.getPositions(tsInd=AssetType.Future)
 if pos is None:
     print '取不到持仓行情信息,退出'
     return
 if pos:
     for i in pos:
         quote = sdk.getQuote(i.code, tsInd=AssetType.Future)
         if i.orderType == 1:
             sdk.makeOrder(i.code, quote.close, i.optPosition, -1, 2)
         if i.orderType == 2:
             sdk.makeOrder(i.code, quote.close, i.optPosition, -2, 2)
         if i.orderType == -1:
             sdk.makeOrder(i.code, quote.close, i.optPosition, 1, 2)
         if i.orderType == -2:
             sdk.makeOrder(i.code, quote.close, i.optPosition, 2, 2)

def main():
# 将策略函数加入
config['initial'] = initial
config['strategy'] = strategy
config['preparePerDay'] = initPerDay
# 启动SDK
SDKCoreEngine(**config).run()

if __name__ == "__main__":
import time
starttime = time.time()
main()
endtime = time.time()
print endtime - starttime