合并vnpy v1.3版本
This commit is contained in:
parent
6c44da565d
commit
f37440e6bb
@ -57,6 +57,9 @@ class BacktestingEngine(object):
|
||||
self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除
|
||||
self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除
|
||||
|
||||
# 引擎类型为回测
|
||||
self.engineType = ENGINETYPE_BACKTESTING
|
||||
|
||||
# 回测相关
|
||||
self.strategy = None # 回测策略
|
||||
self.mode = self.BAR_MODE # 回测模式,默认为K线
|
||||
@ -401,8 +404,7 @@ class BacktestingEngine(object):
|
||||
else:
|
||||
self.writeCtaLog(u'MysqlDB未连接,请检查')
|
||||
|
||||
except MySQLdb.Error, e:
|
||||
|
||||
except MySQLdb.Error as e:
|
||||
self.writeCtaLog(u'MysqlDB载入数据失败,请检查.Error {0}'.format(e))
|
||||
|
||||
def __dataToTick(self, data):
|
||||
@ -494,15 +496,14 @@ class BacktestingEngine(object):
|
||||
else:
|
||||
self.writeCtaLog(u'MysqlDB未连接,请检查')
|
||||
|
||||
except MySQLdb.Error, e:
|
||||
|
||||
except MySQLdb.Error as e:
|
||||
self.writeCtaLog(u'MysqlDB载入数据失败,请检查.Error {0}: {1}'.format(e.arg[0],e.arg[1]))
|
||||
|
||||
# 出错后缺省返回
|
||||
return startDate-timedelta(days=3)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
def runBackTestingWithArbTickFile(self, arbSymbol):
|
||||
def runBackTestingWithArbTickFile(self,mainPath, arbSymbol):
|
||||
"""运行套利回测(使用本地tickcsv数据)
|
||||
参数:套利代码 SP rb1610&rb1701
|
||||
added by IncenseLee
|
||||
@ -567,14 +568,24 @@ class BacktestingEngine(object):
|
||||
self.output(u'回测日期:{0}'.format(testday))
|
||||
|
||||
# 白天数据
|
||||
self.__loadArbTicks(u'SHFE',testday,leg1,leg2)
|
||||
self.__loadArbTicks(mainPath,testday,leg1,leg2)
|
||||
|
||||
# 夜盘数据
|
||||
self.__loadArbTicks(u'SHFE_night', testday, leg1, leg2)
|
||||
self.__loadArbTicks(mainPath+'_night', testday, leg1, leg2)
|
||||
|
||||
|
||||
def __loadArbTicks(self,mainPath,testday,leg1,leg2):
|
||||
|
||||
self.writeCtaLog(u'加载回测日期:{0}\{1}的价差tick'.format(mainPath, testday))
|
||||
|
||||
cachefilename = u'{0}_{1}_{2}_{3}_{4}'.format(self.symbol,leg1,leg2, mainPath, testday.strftime('%Y%m%d'))
|
||||
|
||||
arbTicks = self.__loadArbTicksFromLocalCache(cachefilename)
|
||||
|
||||
dt = None
|
||||
|
||||
if len(arbTicks) < 1:
|
||||
|
||||
leg1File = u'z:\\ticks\\{0}\\{1}\\{2}\\{3}\\{4}.txt' \
|
||||
.format(mainPath, testday.strftime('%Y%m'), self.symbol, testday.strftime('%m%d'), leg1)
|
||||
if not os.path.isfile(leg1File):
|
||||
@ -587,18 +598,12 @@ class BacktestingEngine(object):
|
||||
self.writeCtaLog(u'{0}文件不存在'.format(leg2File))
|
||||
return
|
||||
|
||||
self.writeCtaLog(u'加载回测日期:{0}\{1}的价差tick'.format(mainPath, testday))
|
||||
|
||||
cachefilename = u'{0}_{1}_{2}_{3}_{4}'.format(self.symbol,leg1,leg2, mainPath, testday.strftime('%Y%m%d'))
|
||||
|
||||
arbTicks = self.__loadArbTicksFromLocalCache(cachefilename)
|
||||
|
||||
if len(arbTicks) < 1:
|
||||
# 先读取leg2的数据到目录,以日期时间为key
|
||||
leg2Ticks = {}
|
||||
|
||||
leg2CsvReadFile = file(leg2File, 'rb')
|
||||
reader = csv.DictReader((line.replace('\0', '') for line in leg2CsvReadFile), delimiter=",")
|
||||
#reader = csv.DictReader((line.replace('\0',' ') for line in leg2CsvReadFile), delimiter=",")
|
||||
reader = csv.DictReader(leg2CsvReadFile, delimiter=",")
|
||||
self.writeCtaLog(u'加载{0}'.format(leg2File))
|
||||
for row in reader:
|
||||
tick = CtaTickData()
|
||||
@ -607,8 +612,26 @@ class BacktestingEngine(object):
|
||||
tick.symbol = self.symbol
|
||||
|
||||
tick.date = testday.strftime('%Y%m%d')
|
||||
tick.tradingDay = tick.date
|
||||
tick.time = row['Time']
|
||||
|
||||
try:
|
||||
tick.datetime = datetime.strptime(tick.date + ' ' + tick.time, '%Y%m%d %H:%M:%S.%f')
|
||||
except Exception as ex:
|
||||
self.writeCtaError(u'日期转换错误:{0},{1}:{2}'.format(tick.date + ' ' + tick.time, Exception, ex))
|
||||
continue
|
||||
|
||||
# 修正毫秒
|
||||
if tick.datetime.replace(microsecond = 0) == dt:
|
||||
# 与上一个tick的时间(去除毫秒后)相同,修改为500毫秒
|
||||
tick.datetime=tick.datetime.replace(microsecond = 500)
|
||||
tick.time = tick.datetime.strftime('%H:%M:%S.%f')
|
||||
|
||||
else:
|
||||
tick.datetime = tick.datetime.replace(microsecond=0)
|
||||
tick.time = tick.datetime.strftime('%H:%M:%S.%f')
|
||||
|
||||
dt = tick.datetime
|
||||
|
||||
tick.lastPrice = float(row['LastPrice'])
|
||||
tick.volume = int(float(row['LVolume']))
|
||||
@ -622,27 +645,49 @@ class BacktestingEngine(object):
|
||||
or (tick.askPrice1 == float('1.79769E308') and tick.askVolume1 == 0):
|
||||
continue
|
||||
|
||||
leg2Ticks[tick.date + ' ' + tick.time] = tick
|
||||
dtStr = tick.date + ' ' + tick.time
|
||||
if dtStr in leg2Ticks:
|
||||
self.writeCtaError(u'日内数据重复,异常,数据时间为:{0}'.format(dtStr))
|
||||
else:
|
||||
leg2Ticks[dtStr] = tick
|
||||
|
||||
leg1CsvReadFile = file(leg1File, 'rb')
|
||||
reader = csv.DictReader((line.replace('\0', '') for line in leg1CsvReadFile), delimiter=",")
|
||||
#reader = csv.DictReader((line.replace('\0',' ') for line in leg1CsvReadFile), delimiter=",")
|
||||
reader = csv.DictReader(leg1CsvReadFile, delimiter=",")
|
||||
self.writeCtaLog(u'加载{0}'.format(leg1File))
|
||||
|
||||
dt = None
|
||||
for row in reader:
|
||||
dtStr = ' '.join([testday.strftime('%Y%m%d'), row['Time']])
|
||||
|
||||
arbTick = CtaTickData()
|
||||
|
||||
arbTick.date = testday.strftime('%Y%m%d')
|
||||
arbTick.time = row['Time']
|
||||
try:
|
||||
arbTick.datetime = datetime.strptime(arbTick.date + ' ' + arbTick.time, '%Y%m%d %H:%M:%S.%f')
|
||||
except Exception as ex:
|
||||
self.writeCtaError(u'日期转换错误:{0},{1}:{2}'.format(arbTick.date + ' ' + arbTick.time, Exception, ex))
|
||||
continue
|
||||
|
||||
# 修正毫秒
|
||||
if arbTick.datetime.replace(microsecond=0) == dt:
|
||||
# 与上一个tick的时间(去除毫秒后)相同,修改为500毫秒
|
||||
arbTick.datetime = arbTick.datetime.replace(microsecond=500)
|
||||
arbTick.time = arbTick.datetime.strftime('%H:%M:%S.%f')
|
||||
|
||||
else:
|
||||
arbTick.datetime = arbTick.datetime.replace(microsecond=0)
|
||||
arbTick.time = arbTick.datetime.strftime('%H:%M:%S.%f')
|
||||
|
||||
dt = arbTick.datetime
|
||||
dtStr = ' '.join([arbTick.date, arbTick.time])
|
||||
|
||||
if dtStr in leg2Ticks:
|
||||
leg2Tick = leg2Ticks[dtStr]
|
||||
|
||||
arbTick = CtaTickData()
|
||||
|
||||
arbTick.vtSymbol = self.symbol
|
||||
arbTick.symbol = self.symbol
|
||||
|
||||
arbTick.date = testday.strftime('%Y%m%d')
|
||||
arbTick.time = row['Time']
|
||||
arbTick.datetime = datetime.strptime(arbTick.date + ' ' + arbTick.time, '%Y%m%d %H:%M:%S.%f')
|
||||
|
||||
arbTick.lastPrice = EMPTY_FLOAT
|
||||
arbTick.volume = EMPTY_INT
|
||||
|
||||
@ -667,6 +712,8 @@ class BacktestingEngine(object):
|
||||
|
||||
arbTicks.append(arbTick)
|
||||
|
||||
del leg2Ticks[dtStr]
|
||||
|
||||
# 保存到历史目录
|
||||
if len(arbTicks) > 0:
|
||||
self.__saveArbTicksToLocalCache(cachefilename, arbTicks)
|
||||
@ -790,7 +837,7 @@ class BacktestingEngine(object):
|
||||
if not (bar.datetime < self.dataStartDate or bar.datetime >= self.dataEndDate):
|
||||
self.newBar(bar)
|
||||
|
||||
except Exception, ex:
|
||||
except Exception as ex:
|
||||
self.writeCtaLog(u'{0}:{1}'.format(Exception,ex))
|
||||
continue
|
||||
|
||||
@ -1109,7 +1156,7 @@ class BacktestingEngine(object):
|
||||
# 从字典中删除该限价单
|
||||
try:
|
||||
del self.workingLimitOrderDict[orderID]
|
||||
except Exception,ex:
|
||||
except Exception as ex:
|
||||
self.writeCtaError(u'{0}:{1}'.format(Exception, ex))
|
||||
|
||||
if self.calculateMode == self.REALTIME_MODE:
|
||||
@ -1187,6 +1234,7 @@ class BacktestingEngine(object):
|
||||
# 从字典中删除该限价单
|
||||
del self.workingStopOrderDict[stopOrderID]
|
||||
|
||||
# 若采用实时计算净值
|
||||
if self.calculateMode == self.REALTIME_MODE:
|
||||
self.realtimeCalculate()
|
||||
|
||||
|
@ -52,6 +52,10 @@ TICK_DB_NAME = 'VnTrader_Tick_Db'
|
||||
DAILY_DB_NAME = 'VnTrader_Daily_Db'
|
||||
MINUTE_DB_NAME = 'VnTrader_1Min_Db'
|
||||
|
||||
# 引擎类型,用于区分当前策略的运行环境
|
||||
ENGINETYPE_BACKTESTING = 'backtesting' # 回测
|
||||
ENGINETYPE_TRADING = 'trading' # 实盘
|
||||
|
||||
# CTA引擎中涉及的数据类定义
|
||||
from vtConstant import EMPTY_UNICODE, EMPTY_STRING, EMPTY_FLOAT, EMPTY_INT, COLOR_EQUAL
|
||||
|
||||
|
@ -61,6 +61,13 @@ class DoubleEmaDemo(CtaTemplate):
|
||||
"""Constructor"""
|
||||
super(DoubleEmaDemo, self).__init__(ctaEngine, setting)
|
||||
|
||||
# 注意策略类中的可变对象属性(通常是list和dict等),在策略初始化时需要重新创建,
|
||||
# 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险,
|
||||
# 策略类中的这些可变对象属性可以选择不写,全都放在__init__下面,写主要是为了阅读
|
||||
# 策略时方便(更多是个编程习惯的选择)
|
||||
self.fastMa = []
|
||||
self.slowMa = []
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onInit(self):
|
||||
"""初始化策略(必须由用户继承实现)"""
|
||||
@ -76,14 +83,14 @@ class DoubleEmaDemo(CtaTemplate):
|
||||
def onStart(self):
|
||||
"""启动策略(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'双EMA演示策略启动')
|
||||
|
||||
self.putEvent()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onStop(self):
|
||||
"""停止策略(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'双EMA演示策略停止')
|
||||
self.putEvent()
|
||||
self.putEvent()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, tick):
|
||||
"""收到行情TICK推送(必须由用户继承实现)"""
|
||||
|
@ -77,6 +77,12 @@ class CtaEngine(object):
|
||||
# key为vtSymbol,value为PositionBuffer对象
|
||||
self.posBufferDict = {}
|
||||
|
||||
# 引擎类型为实盘
|
||||
self.engineType = ENGINETYPE_TRADING
|
||||
|
||||
# tick缓存
|
||||
self.tickDict = {}
|
||||
|
||||
# 注册事件监听
|
||||
self.registerEvent()
|
||||
|
||||
@ -113,12 +119,19 @@ class CtaEngine(object):
|
||||
# 如果获取持仓缓存失败,则默认平昨
|
||||
if not posBuffer:
|
||||
req.offset = OFFSET_CLOSE
|
||||
# 否则如果有多头今仓,则使用平今
|
||||
elif posBuffer.longToday:
|
||||
req.offset= OFFSET_CLOSETODAY
|
||||
# 其他情况使用平昨
|
||||
else:
|
||||
|
||||
# modified by IncenseLee 2016/11/08,改为优先平昨仓
|
||||
elif posBuffer.longYd :
|
||||
req.offset = OFFSET_CLOSE
|
||||
else:
|
||||
req.offset = OFFSET_CLOSETODAY
|
||||
|
||||
# 否则如果有多头今仓,则使用平今
|
||||
#elif posBuffer.longToday:
|
||||
# req.offset= OFFSET_CLOSETODAY
|
||||
# 其他情况使用平昨
|
||||
#else:
|
||||
# req.offset = OFFSET_CLOSE
|
||||
|
||||
elif orderType == CTAORDER_SHORT:
|
||||
req.direction = DIRECTION_SHORT
|
||||
@ -136,12 +149,19 @@ class CtaEngine(object):
|
||||
# 如果获取持仓缓存失败,则默认平昨
|
||||
if not posBuffer:
|
||||
req.offset = OFFSET_CLOSE
|
||||
# 否则如果有空头今仓,则使用平今
|
||||
elif posBuffer.shortToday:
|
||||
req.offset= OFFSET_CLOSETODAY
|
||||
# 其他情况使用平昨
|
||||
else:
|
||||
|
||||
#modified by IncenseLee 2016/11/08,改为优先平昨仓
|
||||
elif posBuffer.shortYd:
|
||||
req.offset = OFFSET_CLOSE
|
||||
else:
|
||||
req.offset = OFFSET_CLOSETODAY
|
||||
|
||||
# 否则如果有空头今仓,则使用平今
|
||||
#elif posBuffer.shortToday:
|
||||
# req.offset= OFFSET_CLOSETODAY
|
||||
# 其他情况使用平昨
|
||||
#else:
|
||||
# req.offset = OFFSET_CLOSE
|
||||
|
||||
vtOrderID = self.mainEngine.sendOrder(req, contract.gatewayName) # 发单
|
||||
|
||||
@ -191,12 +211,18 @@ class CtaEngine(object):
|
||||
self.writeCtaLog(u'从所有订单{0}中撤销{1}'.format(len(l), symbol))
|
||||
|
||||
for order in l:
|
||||
|
||||
if symbol == EMPTY_STRING:
|
||||
symbolCond = True
|
||||
else:
|
||||
symbolCond = order.symbol == symbol
|
||||
|
||||
if offset == EMPTY_STRING:
|
||||
offsetCond = True
|
||||
else:
|
||||
offsetCond = order.offset == offset
|
||||
|
||||
if order.symbol == symbol and offsetCond:
|
||||
if symbolCond and offsetCond:
|
||||
req = VtCancelOrderReq()
|
||||
req.symbol = order.symbol
|
||||
req.exchange = order.exchange
|
||||
@ -304,6 +330,9 @@ class CtaEngine(object):
|
||||
# 1. 获取事件的Tick数据
|
||||
tick = event.dict_['data']
|
||||
|
||||
# 缓存最新tick
|
||||
self.tickDict[tick.vtSymbol] = tick
|
||||
|
||||
# 2.收到tick行情后,优先处理本地停止单(检查是否要立即发出)
|
||||
self.processStopOrder(tick)
|
||||
|
||||
@ -327,7 +356,6 @@ class CtaEngine(object):
|
||||
ctaTick.datetime = datetime.strptime(' '.join([today, tick.time]), '%Y%m%d %H:%M:%S.%f')
|
||||
ctaTick.date = today
|
||||
|
||||
|
||||
# 逐个推送到策略实例中
|
||||
l = self.tickStrategyDict[tick.vtSymbol]
|
||||
for strategy in l:
|
||||
@ -484,7 +512,7 @@ class CtaEngine(object):
|
||||
try:
|
||||
name = setting['name']
|
||||
className = setting['className']
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.writeCtaLog(u'载入策略出错:%s' %e)
|
||||
return
|
||||
|
||||
@ -750,9 +778,3 @@ class PositionBuffer(object):
|
||||
else:
|
||||
self.longPosition -= trade.volume
|
||||
self.longYd -= trade.volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -196,6 +196,11 @@ class CtaLineBar(object):
|
||||
self.lineUpperBand = [] # 上轨
|
||||
self.lineMiddleBand = [] # 中线
|
||||
self.lineLowerBand = [] # 下轨
|
||||
self.lineBollStd =[] # 标准差
|
||||
|
||||
self.lastBollUpper = EMPTY_FLOAT # 最后一根K的Boll上轨数值(与MinDiff取整)
|
||||
self.lastBollMiddle = EMPTY_FLOAT # 最后一根K的Boll中轨数值(与MinDiff取整)
|
||||
self.lastBollLower = EMPTY_FLOAT # 最后一根K的Boll下轨数值(与MinDiff取整+1)
|
||||
|
||||
# K线的KDJ指标计算数据
|
||||
self.inputKdjLen = EMPTY_INT # KDJ指标的长度,缺省是9
|
||||
@ -497,12 +502,14 @@ class CtaLineBar(object):
|
||||
or (tick.datetime.hour == 2 and tick.datetime.minute == 30):
|
||||
endtick = True
|
||||
|
||||
# 夜盘1:30收盘
|
||||
if self.shortSymbol in NIGHT_MARKET_SQ2 and tick.datetime.hour == 1 and tick.datetime.minute == 00:
|
||||
endtick = True
|
||||
|
||||
# 夜盘23:00收盘
|
||||
if self.shortSymbol in NIGHT_MARKET_SQ3 and tick.datetime.hour == 23 and tick.datetime.minute == 00:
|
||||
endtick = True
|
||||
|
||||
# 夜盘23:30收盘
|
||||
if self.shortSymbol in NIGHT_MARKET_ZZ or self.shortSymbol in NIGHT_MARKET_DL:
|
||||
if tick.datetime.hour == 23 and tick.datetime.minute == 30:
|
||||
endtick = True
|
||||
@ -1109,10 +1116,25 @@ class CtaLineBar(object):
|
||||
del self.lineMiddleBand[0]
|
||||
if len(self.lineLowerBand) > self.inputBollLen*8:
|
||||
del self.lineLowerBand[0]
|
||||
if len(self.lineBollStd) > self.inputBollLen * 8:
|
||||
del self.lineBollStd[0]
|
||||
|
||||
# 1标准差
|
||||
std = (upper[-1] - lower[-1]) / (self.inputBollStdRate*2)
|
||||
self.lineBollStd.append(std)
|
||||
|
||||
u = round(upper[-1], 2)
|
||||
self.lineUpperBand.append(u) # 上轨
|
||||
self.lastBollUpper = u - u % self.minDiff # 上轨取整
|
||||
|
||||
m = round(middle[-1], 2)
|
||||
self.lineMiddleBand.append(m) # 中轨
|
||||
self.lastBollMiddle = m - m % self.minDiff # 中轨取整
|
||||
|
||||
l = round(lower[-1], 2)
|
||||
self.lineLowerBand.append(l) # 下轨
|
||||
self.lastBollLower = l - l % self.minDiff # 下轨取整
|
||||
|
||||
self.lineUpperBand.append(upper[-1])
|
||||
self.lineMiddleBand.append(middle[-1])
|
||||
self.lineLowerBand.append(lower[-1])
|
||||
|
||||
def __recountKdj(self):
|
||||
"""KDJ指标"""
|
||||
@ -1277,3 +1299,232 @@ class CtaLineBar(object):
|
||||
if DEBUGCTALOG:
|
||||
self.strategy.writeCtaLog(u'['+self.name+u'-DEBUG]'+content)
|
||||
|
||||
|
||||
class CtaDayBar(object):
|
||||
"""日线"""
|
||||
|
||||
# 区别:
|
||||
# -使用tick模式时,当tick到达后,最新一个lineBar[-1]是当前的正在拟合的bar,不断累积tick,传统按照OnBar来计算的话,是使用LineBar[-2]。
|
||||
# -使用bar模式时,当一个bar到达时,lineBar[-1]是当前生成出来的Bar,不再更新
|
||||
TICK_MODE = 'tick'
|
||||
BAR_MODE = 'bar'
|
||||
|
||||
# 参数列表,保存了参数的名称
|
||||
paramList = ['vtSymbol']
|
||||
|
||||
def __init__(self, strategy, onBarFunc, setting=None, ):
|
||||
|
||||
self.paramList.append('inputPreLen')
|
||||
self.paramList.append('minDiff')
|
||||
self.paramList.append('shortSymbol')
|
||||
self.paramList.append('name')
|
||||
|
||||
# 输入参数
|
||||
self.name = u'DayBar'
|
||||
|
||||
self.mode = self.TICK_MODE
|
||||
self.inputPreLen = EMPTY_INT # 1
|
||||
|
||||
# OnBar事件回调函数
|
||||
self.onBarFunc = onBarFunc
|
||||
|
||||
self.lineBar = []
|
||||
|
||||
self.currTick = None
|
||||
self.lastTick = None
|
||||
|
||||
self.shortSymbol = EMPTY_STRING # 商品的短代码
|
||||
self.minDiff = 1 # 商品的最小价格单位
|
||||
|
||||
|
||||
def onTick(self, tick):
|
||||
"""行情更新"""
|
||||
|
||||
if self.currTick is None:
|
||||
self.currTick = tick
|
||||
|
||||
self.__drawLineBar(tick)
|
||||
|
||||
self.lastTick = tick
|
||||
|
||||
|
||||
def addBar(self, bar):
|
||||
"""予以外部初始化程序增加bar"""
|
||||
l1 = len(self.lineBar)
|
||||
|
||||
if l1 == 0:
|
||||
bar.datetme = bar.datetime.replace(minute=0, second=0)
|
||||
bar.time = bar.datetime.strftime('%H:%M:%S')
|
||||
self.lineBar.append(bar)
|
||||
return
|
||||
|
||||
# 与最后一个BAR的时间比对,判断是否超过K线的周期
|
||||
lastBar = self.lineBar[-1]
|
||||
|
||||
if bar.tradingDay != lastBar.datetime:
|
||||
bar.datetme = bar.datetime.replace(minute=0, second=0)
|
||||
bar.time = bar.datetime.strftime('%H:%M:%S')
|
||||
self.lineBar.append(bar)
|
||||
self.onBar(lastBar)
|
||||
return
|
||||
|
||||
# 更新最后一个bar
|
||||
# 此段代码,针对一部分短周期生成长周期的k线更新,如3根5分钟k线,合并成1根15分钟k线。
|
||||
lastBar.close = bar.close
|
||||
lastBar.high = max(lastBar.high, bar.high)
|
||||
lastBar.low = min(lastBar.low, bar.low)
|
||||
|
||||
|
||||
lastBar.mid4 = round((2 * lastBar.close + lastBar.high + lastBar.low) / 4, 2)
|
||||
lastBar.mid5 = round((2 * lastBar.close + lastBar.open + lastBar.high + lastBar.low) / 5, 2)
|
||||
|
||||
def __firstTick(self, tick):
|
||||
""" K线的第一个Tick数据"""
|
||||
self.bar = CtaBarData() # 创建新的K线
|
||||
|
||||
self.bar.vtSymbol = tick.vtSymbol
|
||||
self.bar.symbol = tick.symbol
|
||||
self.bar.exchange = tick.exchange
|
||||
self.bar.openInterest = tick.openInterest
|
||||
|
||||
self.bar.open = tick.lastPrice # O L H C
|
||||
self.bar.high = tick.lastPrice
|
||||
self.bar.low = tick.lastPrice
|
||||
self.bar.close = tick.lastPrice
|
||||
|
||||
# K线的日期时间
|
||||
self.bar.tradingDay = tick.tradingDay # K线所在的交易日期
|
||||
self.bar.date = tick.date # K线的日期,(夜盘的话,与交易日期不同哦)
|
||||
|
||||
self.bar.datetime = tick.datetime
|
||||
# K线的日期时间(去除分钟、秒)设为第一个Tick的时间
|
||||
self.bar.datetime = self.bar.datetime.replace(minute=0, second=0, microsecond=0)
|
||||
self.bar.time = self.bar.datetime.strftime('%H:%M:%S')
|
||||
|
||||
self.barFirstTick = True # 标识该Tick属于该Bar的第一个tick数据
|
||||
|
||||
self.lineBar.append(self.bar) # 推入到lineBar队列
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
def __drawLineBar(self, tick):
|
||||
"""生成 line Bar """
|
||||
l1 = len(self.lineBar)
|
||||
# 保存第一个K线数据
|
||||
if l1 == 0:
|
||||
self.__firstTick(tick)
|
||||
return
|
||||
|
||||
# 清除480周期前的数据,
|
||||
if l1 > 60 * 8:
|
||||
del self.lineBar[0]
|
||||
|
||||
# 与最后一个BAR的时间比对,判断是否超过5分钟
|
||||
lastBar = self.lineBar[-1]
|
||||
|
||||
# 处理日内的间隔时段最后一个tick,如10:15分,11:30分,15:00 和 2:30分
|
||||
endtick = False
|
||||
if (tick.datetime.hour == 10 and tick.datetime.minute == 15) \
|
||||
or (tick.datetime.hour == 11 and tick.datetime.minute == 30) \
|
||||
or (tick.datetime.hour == 15 and tick.datetime.minute == 00) \
|
||||
or (tick.datetime.hour == 2 and tick.datetime.minute == 30):
|
||||
endtick = True
|
||||
|
||||
# 夜盘1:30收盘
|
||||
if self.shortSymbol in NIGHT_MARKET_SQ2 and tick.datetime.hour == 1 and tick.datetime.minute == 00:
|
||||
endtick = True
|
||||
|
||||
# 夜盘23:00收盘
|
||||
if self.shortSymbol in NIGHT_MARKET_SQ3 and tick.datetime.hour == 23 and tick.datetime.minute == 00:
|
||||
endtick = True
|
||||
# 夜盘23:30收盘
|
||||
if self.shortSymbol in NIGHT_MARKET_ZZ or self.shortSymbol in NIGHT_MARKET_DL:
|
||||
if tick.datetime.hour == 23 and tick.datetime.minute == 30:
|
||||
endtick = True
|
||||
|
||||
# 满足时间要求,tick的时间(夜盘21点;或者日盘9点,上一个tick为日盘收盘时间
|
||||
if (tick.datetime.hour == 21 or tick.datetime.hour == 9 ) and 14 <= self.lastTick.datetime.hour <= 15:
|
||||
# 创建并推入新的Bar
|
||||
self.__firstTick(tick)
|
||||
# 触发OnBar事件
|
||||
self.onBar(lastBar)
|
||||
|
||||
else:
|
||||
# 更新当前最后一个bar
|
||||
self.barFirstTick = False
|
||||
# 更新最高价、最低价、收盘价、成交量
|
||||
lastBar.high = max(lastBar.high, tick.lastPrice)
|
||||
lastBar.low = min(lastBar.low, tick.lastPrice)
|
||||
lastBar.close = tick.lastPrice
|
||||
|
||||
|
||||
# 更新Bar的颜色
|
||||
if lastBar.close > lastBar.open:
|
||||
lastBar.color = COLOR_RED
|
||||
elif lastBar.close < lastBar.open:
|
||||
lastBar.color = COLOR_BLUE
|
||||
else:
|
||||
lastBar.color = COLOR_EQUAL
|
||||
|
||||
def displayLastBar(self):
|
||||
"""显示最后一个Bar的信息"""
|
||||
msg = u'[' + self.name + u']'
|
||||
|
||||
if len(self.lineBar) < 2:
|
||||
return msg
|
||||
|
||||
if self.mode == self.TICK_MODE:
|
||||
displayBar = self.lineBar[-2]
|
||||
else:
|
||||
displayBar = self.lineBar[-1]
|
||||
|
||||
msg = msg + u'{0} o:{1};h{2};l:{3};c:{4}'. \
|
||||
format(displayBar.date + ' ' + displayBar.time, displayBar.open, displayBar.high,
|
||||
displayBar.low, displayBar.close)
|
||||
|
||||
return msg
|
||||
|
||||
def onBar(self, bar):
|
||||
"""OnBar事件"""
|
||||
|
||||
self.__recountPreHighLow()
|
||||
|
||||
# 回调上层调用者
|
||||
self.onBarFunc(bar)
|
||||
# ----------------------------------------------------------------------
|
||||
def __recountPreHighLow(self):
|
||||
"""计算 K线的前周期最高和最低"""
|
||||
|
||||
if self.inputPreLen <= 0: return # 不计算
|
||||
|
||||
# 1、lineBar满足长度才执行计算
|
||||
if len(self.lineBar) < self.inputPreLen:
|
||||
self.writeCtaLog(u'数据未充分,当前{0}r数据数量:{1},计算High、Low需要:{2}'.
|
||||
format(self.name, len(self.lineBar), self.inputPreLen))
|
||||
return
|
||||
|
||||
# 2.计算前inputPreLen周期内(不包含当前周期)的Bar高点和低点
|
||||
preHigh = EMPTY_FLOAT
|
||||
preLow = EMPTY_FLOAT
|
||||
|
||||
if self.mode == self.TICK_MODE:
|
||||
idx = 2
|
||||
else:
|
||||
idx = 1
|
||||
|
||||
for i in range(len(self.lineBar)-idx, len(self.lineBar)-idx-self.inputPreLen, -1):
|
||||
|
||||
if self.lineBar[i].high > preHigh or preHigh == EMPTY_FLOAT:
|
||||
preHigh = self.lineBar[i].high # 前InputPreLen周期高点
|
||||
|
||||
if self.lineBar[i].low < preLow or preLow == EMPTY_FLOAT:
|
||||
preLow = self.lineBar[i].low # 前InputPreLen周期低点
|
||||
|
||||
# 保存
|
||||
if len(self.preHigh) > self.inputPreLen * 8:
|
||||
del self.preHigh[0]
|
||||
self.preHigh.append(preHigh)
|
||||
|
||||
# 保存
|
||||
if len(self.preLow)> self.inputPreLen * 8:
|
||||
del self.preLow[0]
|
||||
self.preLow.append(preLow)
|
@ -73,7 +73,6 @@ class CtaPosition:
|
||||
"""平、减仓"""
|
||||
|
||||
if direction == DIRECTION_LONG: # 平空仓 Cover
|
||||
|
||||
if self.pos + vol > 0:
|
||||
self.writeCtaLog(u'异常,超出仓位,当前仓位:{0},平仓:{1}'.format(self.pos,vol))
|
||||
self.strategy.pos = self.pos
|
||||
@ -84,7 +83,6 @@ class CtaPosition:
|
||||
self.strategy.pos = self.pos
|
||||
|
||||
if direction == DIRECTION_SHORT: # 平多仓
|
||||
|
||||
if self.pos - vol < 0 :
|
||||
self.writeCtaLog(u'异常,超出仓位,当前仓位:{0},加仓:{1}'.format(self.pos, vol))
|
||||
self.strategy.pos = self.pos
|
||||
|
@ -8,13 +8,15 @@
|
||||
在CTA_setting.json中写入具体每个策略对象的类和合约设置。
|
||||
'''
|
||||
|
||||
from ctaTemplate import DataRecorder
|
||||
from ctaDemo import DoubleEmaDemo
|
||||
#from ctaTemplate import DataRecorder
|
||||
#from ctaDemo import DoubleEmaDemo
|
||||
from strategy22_ArbitrageGrid import Strategy22
|
||||
from strategy24_M15RB import Strategy24
|
||||
from strategy25_NonStdArbitrageGrid import Strategy25
|
||||
|
||||
STRATEGY_CLASS = {}
|
||||
STRATEGY_CLASS['DataRecorder'] = DataRecorder
|
||||
STRATEGY_CLASS['DoubleEmaDemo'] = DoubleEmaDemo
|
||||
#STRATEGY_CLASS['DataRecorder'] = DataRecorder
|
||||
#STRATEGY_CLASS['DoubleEmaDemo'] = DoubleEmaDemo
|
||||
STRATEGY_CLASS['Strategy22'] = Strategy22
|
||||
STRATEGY_CLASS['Strategy24'] = Strategy24
|
||||
STRATEGY_CLASS['Strategy25'] = Strategy25
|
@ -258,107 +258,8 @@ class CtaTemplate(object):
|
||||
"""发出策略状态变化事件"""
|
||||
self.ctaEngine.putStrategyEvent(self.name)
|
||||
|
||||
|
||||
########################################################################
|
||||
class DataRecorder(CtaTemplate):
|
||||
"""
|
||||
纯粹用来记录历史数据的工具(基于CTA策略),
|
||||
建议运行在实际交易程序外的一个vn.trader实例中,
|
||||
本工具会记录Tick和1分钟K线数据。
|
||||
"""
|
||||
className = 'DataRecorder'
|
||||
author = u'用Python的交易员'
|
||||
|
||||
# 策略的基本参数
|
||||
name = EMPTY_UNICODE # 策略实例名称
|
||||
vtSymbol = EMPTY_STRING # 交易的合约vt系统代码
|
||||
|
||||
# 策略的变量
|
||||
bar = None # K线数据对象
|
||||
barMinute = EMPTY_STRING # 当前的分钟,初始化设为-1
|
||||
|
||||
# 变量列表,保存了变量的名称
|
||||
varList = ['inited',
|
||||
'trading',
|
||||
'pos',
|
||||
'barMinute']
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, ctaEngine, setting):
|
||||
"""Constructor"""
|
||||
super(DataRecorder, self).__init__(ctaEngine, setting)
|
||||
def getEngineType(self):
|
||||
"""查询当前运行的环境"""
|
||||
return self.ctaEngine.engineType
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onInit(self):
|
||||
"""初始化"""
|
||||
self.writeCtaLog(u'数据记录工具初始化')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onStart(self):
|
||||
"""启动策略(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'数据记录工具启动')
|
||||
self.putEvent()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onStop(self):
|
||||
"""停止策略(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'数据记录工具停止')
|
||||
self.putEvent()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, tick):
|
||||
"""收到行情TICK推送"""
|
||||
# 收到Tick后,首先插入到数据库里
|
||||
self.insertTick(tick)
|
||||
|
||||
# 计算K线
|
||||
tickMinute = tick.datetime.minute
|
||||
|
||||
if tickMinute != self.barMinute: # 如果分钟变了,则把旧的K线插入数据库,并生成新的K线
|
||||
if self.bar:
|
||||
self.onBar(self.bar)
|
||||
|
||||
bar = CtaBarData() # 创建新的K线,目的在于防止之前K线对象在插入Mongo中被再次修改,导致出错
|
||||
bar.vtSymbol = tick.vtSymbol
|
||||
bar.symbol = tick.symbol
|
||||
bar.exchange = tick.exchange
|
||||
|
||||
bar.open = tick.lastPrice
|
||||
bar.high = tick.lastPrice
|
||||
bar.low = tick.lastPrice
|
||||
bar.close = tick.lastPrice
|
||||
|
||||
bar.date = tick.date
|
||||
bar.time = tick.time
|
||||
bar.datetime = tick.datetime # K线的时间设为第一个Tick的时间
|
||||
|
||||
bar.volume = tick.volume
|
||||
bar.openInterest = tick.openInterest
|
||||
|
||||
self.bar = bar # 这种写法为了减少一层访问,加快速度
|
||||
self.barMinute = tickMinute # 更新当前的分钟
|
||||
|
||||
else: # 否则继续累加新的K线
|
||||
bar = self.bar # 写法同样为了加快速度
|
||||
|
||||
bar.high = max(bar.high, tick.lastPrice)
|
||||
bar.low = min(bar.low, tick.lastPrice)
|
||||
bar.close = tick.lastPrice
|
||||
|
||||
bar.volume = bar.volume + tick.volume # 成交量是累加的
|
||||
bar.openInterest = tick.openInterest # 持仓量直接更新
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOrder(self, order):
|
||||
"""收到委托变化推送"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTrade(self, trade):
|
||||
"""收到成交推送"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onBar(self, bar):
|
||||
"""收到Bar推送"""
|
||||
self.insertBar(bar)
|
@ -8,6 +8,7 @@ CTA模块相关的GUI控制组件
|
||||
from uiBasicWidget import QtGui, QtCore, BasicCell
|
||||
from eventEngine import *
|
||||
from time import sleep
|
||||
import os
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -54,6 +55,8 @@ class CtaValueMonitor(QtGui.QTableWidget):
|
||||
cell = self.keyCellDict[k]
|
||||
cell.setText(unicode(v))
|
||||
|
||||
self.resizeColumnsToContents()
|
||||
self.resizeRowsToContents()
|
||||
|
||||
########################################################################
|
||||
class CtaStrategyManager(QtGui.QGroupBox):
|
||||
@ -183,7 +186,9 @@ class CtaEngineManager(QtGui.QWidget):
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setWindowTitle(u'CTA策略')
|
||||
path = os.getcwd().rsplit('\\')[-1]
|
||||
|
||||
self.setWindowTitle(u'{0} CTA策略'.format(path))
|
||||
|
||||
# 按钮
|
||||
loadButton = QtGui.QPushButton(u'加载策略')
|
||||
|
Loading…
Reference in New Issue
Block a user