2017-04-09 12:25:20 +00:00
|
|
|
|
# encoding: UTF-8
|
|
|
|
|
|
|
|
|
|
from ctaBase import *
|
|
|
|
|
from vtConstant import *
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
DEBUGCTALOG = True
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
网格交易,用于套利单
|
|
|
|
|
作者:李来佳,QQ/Wechat:28888502
|
|
|
|
|
ChangeLog:
|
|
|
|
|
0713,修改closeGrid,增加volume字段,关闭网格时,根据价格和交易量进行双重匹配.
|
|
|
|
|
0715,增加保存json和重启后加载本地json文件
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class CtaGrid(object):
|
|
|
|
|
"""网格类
|
|
|
|
|
它是网格交易的最小单元
|
|
|
|
|
包括交易方向,开仓价格,平仓价格,止损价格,开仓状态,平仓状态
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, direction, openprice, closeprice, stopprice=EMPTY_FLOAT, volume=1):
|
|
|
|
|
|
2017-04-28 14:10:07 +00:00
|
|
|
|
self.direction = direction # 交易方向(LONG:多,正套;SHORT:空,反套)
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.openPrice = openprice # 开仓价格
|
|
|
|
|
self.closePrice = closeprice # 平仓价格
|
|
|
|
|
self.stopPrice = stopprice # 止损价格
|
|
|
|
|
|
|
|
|
|
self.volume = volume # 开仓数量
|
|
|
|
|
self.tradedVolume = EMPTY_INT # 成交数量 开仓时,为开仓数量,平仓时,为平仓数量
|
|
|
|
|
|
|
|
|
|
self.orderStatus = False # 挂单状态: True,已挂单,False,未挂单
|
|
|
|
|
self.orderRef = EMPTY_STRING # OrderId
|
|
|
|
|
self.openStatus = False # 开仓状态
|
|
|
|
|
self.closeStatus = False # 平仓状态
|
|
|
|
|
|
|
|
|
|
self.openDatetime = None
|
|
|
|
|
self.orderDatetime = None # 委托时间
|
|
|
|
|
|
2017-06-03 12:12:54 +00:00
|
|
|
|
self.lockGrids = [] # 锁单的网格,[openPrice,openPrice]
|
|
|
|
|
|
2017-04-09 12:25:20 +00:00
|
|
|
|
def toJson(self):
|
|
|
|
|
"""输出JSON"""
|
|
|
|
|
|
|
|
|
|
j = {}
|
|
|
|
|
j['direction'] = self.direction
|
|
|
|
|
j['openPrice'] = self.openPrice # 开仓价格
|
|
|
|
|
j['closePrice'] = self.closePrice # 平仓价格
|
|
|
|
|
j['stopPrice'] = self.stopPrice # 止损价格
|
|
|
|
|
|
|
|
|
|
j['volume'] = self.volume # 开仓数量
|
|
|
|
|
|
|
|
|
|
j['tradedVolume'] = self.tradedVolume # 成交数量
|
|
|
|
|
|
|
|
|
|
j['orderStatus'] = self.orderStatus # 挂单状态: True,已挂单,False,未挂单
|
|
|
|
|
j['orderRef'] = self.orderRef # OrderId
|
|
|
|
|
j['openStatus'] = self.openStatus # 开仓状态
|
|
|
|
|
j['closeStatus'] = self.closeStatus # 平仓状态
|
2017-06-03 12:12:54 +00:00
|
|
|
|
j['lockGrids'] = self.lockGrids # 对锁的网格
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
if type(self.openDatetime) == type(None):
|
|
|
|
|
j['openDatetime'] = EMPTY_STRING
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
j['openDatetime'] = self.openDatetime.strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
|
except Exception:
|
|
|
|
|
j['openDatetime'] = EMPTY_STRING
|
|
|
|
|
|
|
|
|
|
return j
|
|
|
|
|
|
|
|
|
|
def toStr(self):
|
|
|
|
|
"""输入字符串"""
|
|
|
|
|
|
|
|
|
|
str = u'o:{0}/{1};c:{2}/{3},r:{4}/opentime:{5}/ordertime:{6}'\
|
|
|
|
|
.format(self.openPrice, self.openStatus, self.closePrice,
|
|
|
|
|
self.closeStatus, self.orderRef, self.openDatetime, self.orderDatetime)
|
|
|
|
|
return str
|
|
|
|
|
|
|
|
|
|
class CtaGridTrade(object):
|
|
|
|
|
"""网格交易类
|
|
|
|
|
包括两个方向的网格队列,
|
2017-05-02 16:07:21 +00:00
|
|
|
|
v1, 基本版
|
|
|
|
|
v2,增加更新最小价格跳动,增加动态上下网格间距
|
2017-04-09 12:25:20 +00:00
|
|
|
|
"""
|
|
|
|
|
|
2017-05-02 16:07:21 +00:00
|
|
|
|
def __init__(self, strategy, maxlots=5, height=2, win=2, vol=1, minDiff = 1):
|
2017-04-09 12:25:20 +00:00
|
|
|
|
"""初始化
|
|
|
|
|
maxlots,最大网格数
|
|
|
|
|
height,网格高度(绝对值,包含minDiff)
|
|
|
|
|
win,盈利数(包含minDiff)
|
|
|
|
|
vol,网格开仓数
|
2017-05-02 16:07:21 +00:00
|
|
|
|
minDiff, 最小价格跳动
|
2017-04-09 12:25:20 +00:00
|
|
|
|
"""
|
2017-05-02 16:07:21 +00:00
|
|
|
|
self.minDiff = minDiff
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
self.strategy = strategy
|
|
|
|
|
|
|
|
|
|
self.jsonName = self.strategy.name #策略名称
|
|
|
|
|
|
|
|
|
|
self.maxLots = maxlots # 缺省网格数量
|
2017-04-28 14:10:07 +00:00
|
|
|
|
self.gridHeight = height # 最小网格高度
|
|
|
|
|
self.gridWin = win # 最小止盈高度
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
self.volume = vol # 每次网格开仓数量
|
|
|
|
|
self.volumeList = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] # 梯级开仓数量比例
|
|
|
|
|
|
|
|
|
|
self.upGrids = [] # 上网格列表,专门做空
|
|
|
|
|
self.dnGrids = [] # 下网格列表,专门做多
|
|
|
|
|
|
2017-04-28 14:10:07 +00:00
|
|
|
|
self.avg_up_open_price = EMPTY_FLOAT # 上网格开仓均价
|
|
|
|
|
self.avg_dn_open_price = EMPTY_FLOAT # 下网格开仓均价
|
|
|
|
|
|
2017-05-02 16:07:21 +00:00
|
|
|
|
self.max_up_open_price = EMPTY_FLOAT # 上网格开仓均价
|
|
|
|
|
self.min_dn_open_price = EMPTY_FLOAT # 下网格开仓均价
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
2017-05-02 16:07:21 +00:00
|
|
|
|
def getVolumeRate(self, gridIndex=EMPTY_INT):
|
|
|
|
|
"""获取网格索引对应的开仓数量比例"""
|
2017-04-09 12:25:20 +00:00
|
|
|
|
if gridIndex >= len(self.volumeList) or gridIndex < 0:
|
|
|
|
|
return 1
|
|
|
|
|
rate = self.volumeList[gridIndex]
|
|
|
|
|
|
|
|
|
|
if rate == 0:
|
|
|
|
|
return 1
|
|
|
|
|
else:
|
|
|
|
|
return rate
|
|
|
|
|
|
|
|
|
|
def initGrid(self, upline=EMPTY_FLOAT, dnline=EMPTY_FLOAT):
|
|
|
|
|
"""初始化网格队列
|
|
|
|
|
upline,上支撑线
|
|
|
|
|
dnline,下阻力线
|
|
|
|
|
"""
|
|
|
|
|
self.writeCtaLog(u'初始化网格队列,upline:{0},dnline:{1}'.format(upline, dnline))
|
|
|
|
|
# 初始化上网格列表
|
|
|
|
|
if len(self.upGrids) == 0:
|
|
|
|
|
|
|
|
|
|
self.upGrids = self.load(direction= DIRECTION_SHORT)
|
|
|
|
|
|
|
|
|
|
if len(self.upGrids) >0:
|
|
|
|
|
self.writeCtaLog(u'上网格从文件加载完成')
|
|
|
|
|
else:
|
|
|
|
|
# 做空,开仓价为上阻力线+网格高度*i,平仓价为开仓价-止盈高度,开仓数量为缺省
|
|
|
|
|
for i in range(0, self.maxLots, 1):
|
|
|
|
|
grid = CtaGrid(direction=DIRECTION_SHORT,
|
|
|
|
|
openprice=upline+self.gridHeight*i,
|
|
|
|
|
closeprice=upline+self.gridHeight*i-self.gridWin,
|
2017-05-02 16:07:21 +00:00
|
|
|
|
volume=self.volume*self.getVolumeRate(i))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.upGrids.append(grid)
|
|
|
|
|
|
|
|
|
|
self.writeCtaLog(u'上网格{0}~{1}初始化完成'.format(upline,upline+self.gridHeight*self.maxLots))
|
|
|
|
|
self.save(direction=DIRECTION_SHORT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化下网格列表
|
|
|
|
|
if len(self.dnGrids) == 0:
|
|
|
|
|
|
|
|
|
|
self.dnGrids = self.load(direction= DIRECTION_LONG)
|
|
|
|
|
|
|
|
|
|
if len(self.dnGrids) >0:
|
|
|
|
|
self.writeCtaLog(u'下网格从文件加载完成')
|
|
|
|
|
else:
|
|
|
|
|
for i in range(0, self.maxLots, 1):
|
|
|
|
|
|
|
|
|
|
# 做多,开仓价为下阻力线-网格高度*i,平仓价为开仓价+止盈高度,开仓数量为缺省
|
|
|
|
|
grid = CtaGrid(direction=DIRECTION_LONG,
|
|
|
|
|
openprice=dnline - self.gridHeight * i,
|
|
|
|
|
closeprice=dnline - self.gridHeight * i + self.gridWin,
|
2017-05-02 16:07:21 +00:00
|
|
|
|
volume=self.volume*self.getVolumeRate(i))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.dnGrids.append(grid)
|
|
|
|
|
|
|
|
|
|
self.writeCtaLog(u'下网格{0}~{1}初始化完成'.format(dnline,dnline-self.gridHeight*self.maxLots))
|
|
|
|
|
self.save(direction=DIRECTION_LONG)
|
|
|
|
|
|
|
|
|
|
def writeCtaLog(self, log):
|
|
|
|
|
self.strategy.writeCtaLog(log)
|
|
|
|
|
|
|
|
|
|
def toStr(self,direction):
|
|
|
|
|
"""显示网格"""
|
|
|
|
|
|
|
|
|
|
pendingCloseList = u'' # 平仓清单
|
|
|
|
|
pendingOpenList = u'' # 开仓清单
|
|
|
|
|
deactiveList = u'' # 待激活清单
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
for grid in self.dnGrids:
|
|
|
|
|
# 待平仓
|
|
|
|
|
if grid.openStatus :
|
|
|
|
|
|
|
|
|
|
if grid.tradedVolume == EMPTY_INT:
|
|
|
|
|
pendingCloseList = pendingCloseList + u'[{0}->{1},v:{2}];'\
|
|
|
|
|
.format(grid.openPrice, grid.closePrice, grid.volume)
|
|
|
|
|
else:
|
|
|
|
|
pendingCloseList = pendingCloseList + u'[{0}->{1},v:{2}/{3}];'\
|
|
|
|
|
.format(grid.openPrice, grid.closePrice, grid.volume, grid.tradedVolume)
|
|
|
|
|
|
|
|
|
|
# 待开仓成交
|
|
|
|
|
elif not grid.openStatus and grid.orderStatus:
|
|
|
|
|
if grid.tradedVolume == EMPTY_INT:
|
|
|
|
|
pendingOpenList = pendingOpenList + u'[{0},v:{1}];'.format(grid.openPrice, grid.volume)
|
|
|
|
|
else:
|
|
|
|
|
pendingOpenList = pendingOpenList + u'[{0},v:{1}/{2}];'\
|
|
|
|
|
.format(grid.openPrice, grid.volume, grid.tradedVolume)
|
|
|
|
|
|
|
|
|
|
# 等待挂单
|
|
|
|
|
else:
|
|
|
|
|
deactiveList = deactiveList + u'[{0}];'.format(grid.openPrice)
|
|
|
|
|
|
|
|
|
|
|
2017-04-28 14:10:07 +00:00
|
|
|
|
return u'多:待平:{0};开:{1};待:{2}'.format(pendingCloseList,pendingOpenList,deactiveList)
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
for grid in self.upGrids:
|
|
|
|
|
# 待平仓
|
|
|
|
|
if grid.openStatus:
|
|
|
|
|
if grid.tradedVolume == EMPTY_INT:
|
|
|
|
|
pendingCloseList = pendingCloseList + u'[{0}->{1},v:{2}];'\
|
|
|
|
|
.format(grid.openPrice, grid.closePrice, grid.volume)
|
|
|
|
|
else:
|
|
|
|
|
pendingCloseList = pendingCloseList + u'[{0}->{1},v:{2}/{3}];'\
|
|
|
|
|
.format(grid.openPrice, grid.closePrice, grid.volume, grid.tradedVolume)
|
|
|
|
|
|
|
|
|
|
# 待开仓成交
|
|
|
|
|
elif not grid.openStatus and grid.orderStatus:
|
|
|
|
|
if grid.tradedVolume == EMPTY_INT:
|
|
|
|
|
pendingOpenList = pendingOpenList + u'[{0},v:{1}];'.format(grid.openPrice, grid.volume)
|
|
|
|
|
else:
|
|
|
|
|
pendingOpenList = pendingOpenList + u'[{0},v:{1}/{2}];'\
|
|
|
|
|
.format(grid.openPrice, grid.volume, grid.tradedVolume)
|
|
|
|
|
|
|
|
|
|
# 等待挂单
|
|
|
|
|
else:
|
|
|
|
|
deactiveList = deactiveList + u'[{0}];'.format(grid.openPrice)
|
|
|
|
|
|
2017-04-28 14:10:07 +00:00
|
|
|
|
return u'空:待平:{0};开:{1};待:{2}'.format(pendingCloseList,pendingOpenList,deactiveList)
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
def getGrids(self, direction, ordered=False, opened=False, closed=False, begin=EMPTY_FLOAT, end=EMPTY_FLOAT):
|
|
|
|
|
"""获取未挂单的网格
|
|
|
|
|
direction:做多、做空方向: 做多方向时,从dnGrids中获取; 做空方向时,从upGrids中获取
|
|
|
|
|
ordered:是否已提交至服务器
|
|
|
|
|
opened:是否已开仓
|
|
|
|
|
closed:是否已平仓
|
|
|
|
|
begin:开始价格,
|
|
|
|
|
end:结束价格,
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# 状态一致,价格大于最低价格
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
if begin == EMPTY_FLOAT: begin = 99999
|
|
|
|
|
if end == EMPTY_FLOAT: end = - 99999
|
|
|
|
|
grids = [x for x in self.dnGrids
|
|
|
|
|
if x.orderStatus == ordered
|
|
|
|
|
and x.openStatus == opened
|
|
|
|
|
and x.closeStatus == closed
|
|
|
|
|
and x.openPrice <= begin
|
|
|
|
|
and x.openPrice >= end]
|
|
|
|
|
|
|
|
|
|
return grids
|
|
|
|
|
|
|
|
|
|
# 状态一致,开仓价格小于最高价格
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
if begin == EMPTY_FLOAT: begin = -99999
|
|
|
|
|
if end == EMPTY_FLOAT: end = 99999
|
|
|
|
|
grids = [x for x in self.upGrids
|
|
|
|
|
if x.orderStatus == ordered
|
|
|
|
|
and x.openStatus == opened
|
|
|
|
|
and x.closeStatus == closed
|
|
|
|
|
and x.openPrice >= begin
|
|
|
|
|
and x.openPrice <= end]
|
|
|
|
|
|
|
|
|
|
return grids
|
|
|
|
|
|
|
|
|
|
def updateOrderRef(self, direction, openPrice, orderRef):
|
|
|
|
|
"""更新网格的orderId"""
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
for x in self.dnGrids:
|
|
|
|
|
if x.openPrice == openPrice:
|
|
|
|
|
x.orderRef = orderRef
|
|
|
|
|
x.orderStatus = True
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
for x in self.upGrids:
|
|
|
|
|
if x.openPrice == openPrice:
|
|
|
|
|
x.orderRef = orderRef
|
|
|
|
|
x.orderStatus = True
|
|
|
|
|
|
|
|
|
|
def cancelOrderRef(self,direction, openPrice):
|
|
|
|
|
"""网格撤单"""
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
for x in self.dnGrids:
|
|
|
|
|
if x.openPrice == openPrice and x.orderRef != EMPTY_STRING and x.orderStatus==True and x.openStatus==False:
|
|
|
|
|
x.orderRef = EMPTY_STRING
|
|
|
|
|
x.orderStatus = False
|
|
|
|
|
self.writeCtaLog(u'下网格撤单[{0}]'.format(x.openPrice))
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
for x in self.upGrids:
|
|
|
|
|
if x.openPrice == openPrice and x.orderRef != EMPTY_STRING and x.orderStatus==True and x.openStatus==False:
|
|
|
|
|
x.orderRef = EMPTY_STRING
|
|
|
|
|
x.orderStatus = False
|
|
|
|
|
self.writeCtaLog(u'上网格撤单[{0}]'.format(x.openPrice))
|
|
|
|
|
|
|
|
|
|
def getGrid(self, direction, openPrice=EMPTY_FLOAT, closePrice=EMPTY_FLOAT, orderRef=EMPTY_STRING, t=EMPTY_STRING):
|
|
|
|
|
"""获取网格"""
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
for x in self.dnGrids:
|
|
|
|
|
# 优先匹配价格
|
|
|
|
|
if t == u'OpenPrice' and x.openPrice == openPrice:
|
|
|
|
|
return x
|
|
|
|
|
elif t == u'ClosePrice' and x.closePrice == closePrice:
|
|
|
|
|
return x
|
|
|
|
|
elif t == u'OrderRef' and x.orderRef == orderRef:
|
|
|
|
|
return x
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
for x in self.upGrids:
|
|
|
|
|
# 优先匹配价格
|
|
|
|
|
if t == u'OpenPrice' and x.openPrice == openPrice:
|
|
|
|
|
return x
|
|
|
|
|
elif t == u'ClosePrice' and x.closePrice == closePrice:
|
|
|
|
|
return x
|
|
|
|
|
elif t == u'OrderRef' and x.orderRef == orderRef:
|
|
|
|
|
return x
|
|
|
|
|
|
|
|
|
|
self.writeCtaLog(u'异常,找不到网格[{0},{1},{2},{3},{4}]'.format(direction, openPrice, closePrice, orderRef, t))
|
|
|
|
|
return None
|
|
|
|
|
|
2017-06-03 12:12:54 +00:00
|
|
|
|
def getLastOpenedGrid(self, direction):
|
|
|
|
|
"""获取最后一个开仓的网格"""
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
opened_short_grids = self.getGrids(direction=direction, opened=True)
|
|
|
|
|
if opened_short_grids is None or len(opened_short_grids) ==0 :
|
|
|
|
|
return None
|
|
|
|
|
if len(opened_short_grids) > 1:
|
|
|
|
|
sortedGrids = sorted(opened_short_grids, key=lambda g:g.openPrice)
|
|
|
|
|
opened_short_grids = sortedGrids[-1:]
|
|
|
|
|
return opened_short_grids[0]
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
opened_long_grids = self.getGrids(direction=direction, opened=True)
|
|
|
|
|
if opened_long_grids is None or len(opened_long_grids) ==0:
|
|
|
|
|
return None
|
|
|
|
|
if len(opened_long_grids) > 1:
|
|
|
|
|
sortedGrids = sorted(opened_long_grids, key=lambda g: g.openPrice)
|
|
|
|
|
opened_long_grids = sortedGrids[0:1]
|
|
|
|
|
return opened_long_grids[0]
|
|
|
|
|
|
2017-04-09 12:25:20 +00:00
|
|
|
|
def closeGrid(self, direction, closePrice, closeVolume):
|
|
|
|
|
"""网格交易结束"""
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
for x in self.dnGrids:
|
|
|
|
|
if x.closePrice == closePrice and x.openStatus and x.volume == closeVolume:
|
|
|
|
|
self.writeCtaLog(u'下网格交易结束[{0}->{1}],仓位:{2},移除网格'.format(x.openPrice, x.closePrice,closeVolume))
|
|
|
|
|
self.dnGrids.remove(x)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if x.closePrice == closePrice and x.openStatus and x.volume > closeVolume:
|
|
|
|
|
self.writeCtaLog(u'下网格交易部分结束[{0}->{1}],减少仓位:{2}'.format(x.openPrice, x.closePrice,closeVolume))
|
|
|
|
|
x.volume = x.volume - closeVolume
|
|
|
|
|
|
|
|
|
|
if x.closePrice == closePrice and x.openStatus and x.volume < closeVolume:
|
|
|
|
|
self.writeCtaLog(u'下网格交易结束[{0}->{1}],移除网格,剩余仓位:{2}'.format(x.openPrice, x.closePrice, closeVolume-x.volume))
|
|
|
|
|
closeVolume = closeVolume - x.volume
|
|
|
|
|
self.dnGrids.remove(x)
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
for x in self.upGrids:
|
|
|
|
|
if x.closePrice == closePrice and x.openStatus and x.volume == closeVolume:
|
|
|
|
|
self.writeCtaLog(u'上网格交易结束[{0}->{1}],仓位:{2},移除网格'.format(x.openPrice, x.closePrice,closeVolume))
|
|
|
|
|
self.upGrids.remove(x)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if x.closePrice == closePrice and x.openStatus and x.volume > closeVolume:
|
|
|
|
|
self.writeCtaLog(u'上网格交易结束[{0}->{1}],仓位减少:{2}'.format(x.openPrice, x.closePrice,closeVolume))
|
|
|
|
|
x.volume = x.volume - closeVolume
|
|
|
|
|
|
|
|
|
|
if x.closePrice == closePrice and x.openStatus and x.volume < closeVolume:
|
|
|
|
|
self.writeCtaLog(u'上网格交易结束[{0}->{1}],移除网格,剩余仓位:{2}'.format(x.openPrice, x.closePrice,closeVolume-x.volume))
|
|
|
|
|
closeVolume = closeVolume - x.volume
|
|
|
|
|
self.upGrids.remove(x)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def removeGrids(self, direction, priceline):
|
|
|
|
|
"""清除网格"""
|
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
for x in self.dnGrids[:]:
|
|
|
|
|
if x.openPrice > priceline and not x.orderStatus and not x.openStatus and not x.closeStatus:
|
|
|
|
|
self.writeCtaLog(u'清除下网格[open={0}]'.format(x.openPrice))
|
|
|
|
|
self.dnGrids.remove(x)
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
for x in self.upGrids[:]:
|
|
|
|
|
if x.openPrice < priceline and not x.orderStatus and not x.openStatus and not x.closeStatus:
|
|
|
|
|
self.writeCtaLog(u'清除上网格[open={0}]'.format(x.openPrice))
|
|
|
|
|
self.upGrids.remove(x)
|
|
|
|
|
|
2017-05-02 16:07:21 +00:00
|
|
|
|
def rebuildGrids(self, direction, upline=EMPTY_FLOAT, dnline=EMPTY_FLOAT, midline=EMPTY_FLOAT, upRate=1, dnRate = 1):
|
2017-04-09 12:25:20 +00:00
|
|
|
|
"""重新拉网
|
|
|
|
|
清除未挂单的网格,
|
|
|
|
|
在上轨/下轨位置重新挂单
|
2017-05-02 16:07:21 +00:00
|
|
|
|
upRate , 上轨网格高度比率
|
|
|
|
|
dnRate, 下轨网格高度比率
|
2017-04-09 12:25:20 +00:00
|
|
|
|
"""
|
|
|
|
|
self.writeCtaLog(u'重新拉网:upline:{0},dnline:{1}'.format(upline, dnline))
|
|
|
|
|
|
2017-05-02 16:07:21 +00:00
|
|
|
|
# 检查上下网格的高度比率,不能低于0.5
|
|
|
|
|
if upRate < 0.5 or dnRate < 0.5:
|
|
|
|
|
upRate = max(0.5, upRate)
|
|
|
|
|
dnRate = max(0.5, dnRate)
|
|
|
|
|
|
2017-04-09 12:25:20 +00:00
|
|
|
|
if direction == DIRECTION_LONG:
|
|
|
|
|
minPriceInOrder = midline
|
|
|
|
|
removePrices = []
|
|
|
|
|
# 移除未挂单的下网格
|
|
|
|
|
for x in self.dnGrids[:]:
|
|
|
|
|
if not x.orderStatus and not x.openStatus and not x.closeStatus:
|
|
|
|
|
removePrices.append(x.openPrice)
|
|
|
|
|
self.dnGrids.remove(x)
|
|
|
|
|
else:
|
|
|
|
|
self.writeCtaLog(u'保留网格[open={0}]'.format(x.openPrice))
|
|
|
|
|
if x.openPrice < minPriceInOrder :
|
|
|
|
|
minPriceInOrder = x.openPrice
|
|
|
|
|
|
|
|
|
|
self.writeCtaLog(u'清除下网格[{0}]'.format(removePrices))
|
|
|
|
|
|
|
|
|
|
# 需要重建的剩余网格数量
|
|
|
|
|
remainLots = len(self.dnGrids)
|
|
|
|
|
lots = self.maxLots - remainLots
|
|
|
|
|
|
2017-05-10 11:30:36 +00:00
|
|
|
|
dnline = min(dnline, minPriceInOrder-self.gridHeight*dnRate)
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.writeCtaLog(u'需要重建的网格数量:{0},起点:{1}'.format(lots, dnline))
|
|
|
|
|
|
|
|
|
|
if lots > 0:
|
|
|
|
|
for i in range(0, lots, 1):
|
|
|
|
|
# 做多,开仓价为下阻力线-网格高度*i,平仓价为开仓价+止盈高度,开仓数量为缺省
|
2017-05-10 11:30:36 +00:00
|
|
|
|
open_price = int((dnline - self.gridHeight * i * dnRate) / self.minDiff ) * self.minDiff
|
2017-05-02 16:07:21 +00:00
|
|
|
|
close_price = int((open_price + self.gridWin* dnRate)/self.minDiff) * self.minDiff
|
|
|
|
|
|
2017-04-09 12:25:20 +00:00
|
|
|
|
grid = CtaGrid(direction=DIRECTION_LONG,
|
2017-05-02 16:07:21 +00:00
|
|
|
|
openprice=open_price,
|
|
|
|
|
closeprice=close_price,
|
|
|
|
|
volume=self.volume*self.getVolumeRate(remainLots + i))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
self.dnGrids.append(grid)
|
|
|
|
|
self.writeCtaLog(u'重新拉下网格:[{0}~{1}]'.format(dnline, dnline-self.gridHeight * lots))
|
|
|
|
|
|
|
|
|
|
if direction == DIRECTION_SHORT:
|
|
|
|
|
maxPriceInOrder = midline
|
|
|
|
|
removePrices = []
|
|
|
|
|
# 移除未挂单的上网格
|
|
|
|
|
for x in self.upGrids[:]:
|
|
|
|
|
if not x.orderStatus and not x.openStatus and not x.closeStatus:
|
|
|
|
|
removePrices.append(x.openPrice)
|
|
|
|
|
self.upGrids.remove(x)
|
|
|
|
|
else:
|
|
|
|
|
self.writeCtaLog(u'保留网格[open={0}]'.format(x.openPrice))
|
|
|
|
|
if x.openPrice > maxPriceInOrder :
|
|
|
|
|
maxPriceInOrder = x.openPrice
|
|
|
|
|
|
|
|
|
|
self.writeCtaLog(u'清除上网格[{0}]'.format(removePrices))
|
|
|
|
|
|
|
|
|
|
# 需要重建的剩余网格数量
|
|
|
|
|
remainLots = len(self.upGrids)
|
|
|
|
|
lots = self.maxLots - remainLots
|
2017-05-10 11:30:36 +00:00
|
|
|
|
upline = max(upline, maxPriceInOrder+self.gridHeight*upRate)
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.writeCtaLog(u'需要重建的网格数量:{0},起点:{1}'.format(lots, upline))
|
|
|
|
|
|
|
|
|
|
if lots > 0:
|
|
|
|
|
# 做空,开仓价为上阻力线+网格高度*i,平仓价为开仓价-止盈高度,开仓数量为缺省
|
|
|
|
|
for i in range(0, lots, 1):
|
2017-05-10 11:30:36 +00:00
|
|
|
|
open_price = int((upline + self.gridHeight * i * upRate) / self.minDiff) * self.minDiff
|
2017-05-02 16:07:21 +00:00
|
|
|
|
close_price = int((open_price - self.gridWin * upRate) / self.minDiff) * self.minDiff
|
|
|
|
|
|
2017-04-09 12:25:20 +00:00
|
|
|
|
grid = CtaGrid(direction=DIRECTION_SHORT,
|
2017-05-02 16:07:21 +00:00
|
|
|
|
openprice=open_price,
|
|
|
|
|
closeprice=close_price,
|
|
|
|
|
volume=self.volume*self.getVolumeRate(remainLots + i))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.upGrids.append(grid)
|
|
|
|
|
|
|
|
|
|
self.writeCtaLog(u'重新拉上网格:[{0}~{1}]'.format(upline, upline+self.gridHeight * lots))
|
|
|
|
|
|
2017-04-28 14:10:07 +00:00
|
|
|
|
def recount_avg_open_price(self):
|
|
|
|
|
"""计算网格的平均开仓价"""
|
2017-05-02 16:07:21 +00:00
|
|
|
|
up_open_list = [x for x in self.upGrids if x.openStatus]
|
|
|
|
|
|
|
|
|
|
self.max_up_open_price = -99999
|
|
|
|
|
self.avg_up_open_price = -99999
|
|
|
|
|
self.min_dn_open_price = 99999
|
|
|
|
|
self.avg_dn_open_price = 99999
|
2017-04-28 14:10:07 +00:00
|
|
|
|
|
|
|
|
|
total_price = EMPTY_FLOAT
|
|
|
|
|
total_volume = EMPTY_INT
|
|
|
|
|
for x in up_open_list:
|
2017-05-02 16:07:21 +00:00
|
|
|
|
self.max_up_open_price = max(self.max_up_open_price, x.openPrice)
|
2017-04-28 14:10:07 +00:00
|
|
|
|
total_price += x.openPrice*x.volume
|
|
|
|
|
total_volume += x.volume
|
|
|
|
|
|
|
|
|
|
if total_volume > 0:
|
|
|
|
|
self.avg_up_open_price = total_price/total_volume
|
|
|
|
|
|
|
|
|
|
total_price = EMPTY_FLOAT
|
|
|
|
|
total_volume = EMPTY_INT
|
|
|
|
|
|
|
|
|
|
dn_open_list = [x for x in self.dnGrids if x.openStatus]
|
|
|
|
|
for x in dn_open_list:
|
2017-05-02 16:07:21 +00:00
|
|
|
|
self.min_dn_open_price = min(self.min_dn_open_price,x.openPrice)
|
2017-04-28 14:10:07 +00:00
|
|
|
|
total_price += x.openPrice*x.volume
|
|
|
|
|
total_volume += x.volume
|
|
|
|
|
|
|
|
|
|
if total_volume > 0:
|
|
|
|
|
self.avg_dn_open_price = total_price/total_volume
|
|
|
|
|
|
|
|
|
|
def save(self, direction):
|
2017-04-09 12:25:20 +00:00
|
|
|
|
"""保存网格至本地Json文件"""
|
2017-05-02 16:07:21 +00:00
|
|
|
|
|
|
|
|
|
# 更新开仓均价
|
|
|
|
|
self.recount_avg_open_price()
|
2017-04-28 14:10:07 +00:00
|
|
|
|
path = os.path.abspath(os.path.dirname(__file__))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
# 保存上网格列表
|
|
|
|
|
if len(self.upGrids) > 0 and direction == DIRECTION_SHORT:
|
2017-04-28 14:10:07 +00:00
|
|
|
|
jsonFileName = os.path.join(path, u'data', u'{0}_upGrids.json'.format(self.jsonName))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
l = []
|
|
|
|
|
for grid in self.upGrids:
|
|
|
|
|
l.append(grid.toJson())
|
|
|
|
|
|
|
|
|
|
with open(jsonFileName, 'w') as f:
|
|
|
|
|
jsonL = json.dumps(l, indent=4)
|
|
|
|
|
f.write(jsonL)
|
|
|
|
|
|
|
|
|
|
#self.writeCtaLog(u'上网格保存文件{0}完成'.format(jsonFileName))
|
|
|
|
|
|
|
|
|
|
# 保存上网格列表
|
|
|
|
|
if len(self.dnGrids) > 0 and direction == DIRECTION_LONG:
|
2017-04-28 14:10:07 +00:00
|
|
|
|
jsonFileName = os.path.join(path, u'data', u'{0}_dnGrids.json'.format(self.jsonName))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
l = []
|
|
|
|
|
for grid in self.dnGrids:
|
|
|
|
|
l.append(grid.toJson())
|
|
|
|
|
|
|
|
|
|
with open(jsonFileName, 'w') as f:
|
|
|
|
|
jsonL = json.dumps(l, indent=4)
|
|
|
|
|
f.write(jsonL)
|
|
|
|
|
|
|
|
|
|
#self.writeCtaLog(u'下网格保存文件{0}完成'.format(jsonFileName))
|
|
|
|
|
|
|
|
|
|
def load(self, direction):
|
|
|
|
|
"""加载本地Json至网格"""
|
|
|
|
|
|
2017-04-28 14:10:07 +00:00
|
|
|
|
path = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
|
|
2017-04-09 12:25:20 +00:00
|
|
|
|
if direction == DIRECTION_SHORT:
|
2017-04-28 14:10:07 +00:00
|
|
|
|
jsonFileName = os.path.join(path, u'data', u'{0}_upGrids.json'.format(self.jsonName))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.writeCtaLog(u'开始加载上网格文件{0}'.format(jsonFileName))
|
|
|
|
|
if direction == DIRECTION_LONG:
|
2017-04-28 14:10:07 +00:00
|
|
|
|
jsonFileName = os.path.join(path, u'data', u'{0}_dnGrids.json'.format(self.jsonName))
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.writeCtaLog(u'开始加载上网格文件{0}'.format(jsonFileName))
|
|
|
|
|
|
2017-04-28 14:10:07 +00:00
|
|
|
|
if not os.path.isfile(jsonFileName):
|
2017-04-09 12:25:20 +00:00
|
|
|
|
self.writeCtaLog(u'网格保存文件{0}不存在'.format(jsonFileName))
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
f = file(jsonFileName)
|
|
|
|
|
except IOError:
|
|
|
|
|
self.writeCtaLog(u'读取网格出错,请检查')
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
# 解析json文件
|
|
|
|
|
l = json.load(f)
|
|
|
|
|
grids = []
|
|
|
|
|
|
|
|
|
|
if len(l) > 0:
|
|
|
|
|
|
|
|
|
|
for i in l:
|
|
|
|
|
closePrice = float(i['closePrice'])
|
|
|
|
|
openPrice = float(i['openPrice'])
|
|
|
|
|
stopPrice = float(i['stopPrice'])
|
|
|
|
|
|
|
|
|
|
self.writeCtaLog(u'load Grid:open:{0},close:{1},stop:{2}'.format(openPrice, closePrice, stopPrice))
|
|
|
|
|
|
|
|
|
|
grid = CtaGrid(direction=i['direction'], openprice=openPrice, closeprice=closePrice,
|
|
|
|
|
stopprice=stopPrice, volume=i['volume'])
|
|
|
|
|
grid.orderStatus = i['orderStatus'] # 挂单状态: True,已挂单,False,未挂单
|
|
|
|
|
grid.orderRef = i['orderRef'] # OrderId
|
|
|
|
|
grid.openStatus = i['openStatus'] # 开仓状态
|
|
|
|
|
grid.closeStatus = i['closeStatus'] # 平仓状态
|
2017-06-03 12:12:54 +00:00
|
|
|
|
|
2017-04-09 12:25:20 +00:00
|
|
|
|
strTime = i['openDatetime']
|
|
|
|
|
if strTime == EMPTY_STRING or type(strTime)==type(None):
|
|
|
|
|
grid.openDatetime = None
|
|
|
|
|
else:
|
|
|
|
|
grid.openDatetime = datetime.strptime(strTime, '%Y-%m-%d %H:%M:%S')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
grid.tradedVolume = i['tradedVolume'] # 已交易的合约数量
|
|
|
|
|
except KeyError:
|
|
|
|
|
grid.tradedVolume = EMPTY_INT
|
2017-06-03 12:12:54 +00:00
|
|
|
|
try:
|
|
|
|
|
grid.lockGrids = i['lockGrids']
|
|
|
|
|
except KeyError:
|
|
|
|
|
grid.lockGrids = []
|
2017-04-09 12:25:20 +00:00
|
|
|
|
|
|
|
|
|
self.writeCtaLog(grid.toStr())
|
|
|
|
|
|
|
|
|
|
grids.append(grid)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.writeCtaLog(u'解析网格出错,设置为空列表')
|
|
|
|
|
|
|
|
|
|
f.close()
|
|
|
|
|
return grids
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|