From fea6ce4aa9456b4300df99e1cd05bfb6f627e370 Mon Sep 17 00:00:00 2001 From: msincenselee Date: Sat, 1 Apr 2017 12:24:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=9B=9E=E6=B5=8B=E5=BC=95?= =?UTF-8?q?=E6=93=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vn.trader/ctaAlgo/ctaBacktesting.py | 43 ++++-- vn.trader/ctaAlgo/ctaBase.py | 4 +- vn.trader/ctaAlgo/ctaLineBar.py | 211 +++++++++++++++++---------- vn.trader/ctaAlgo/ctaPosition.py | 9 -- vn.trader/ctaAlgo/ctaSetting.py | 6 +- vn.trader/ctaAlgo/sinaClient.py | 214 ---------------------------- vn.trader/ctaAlgo/uiCtaWidget.py | 13 +- 7 files changed, 184 insertions(+), 316 deletions(-) delete mode 100644 vn.trader/ctaAlgo/sinaClient.py diff --git a/vn.trader/ctaAlgo/ctaBacktesting.py b/vn.trader/ctaAlgo/ctaBacktesting.py index 9d1bcedd..a8217c6f 100644 --- a/vn.trader/ctaAlgo/ctaBacktesting.py +++ b/vn.trader/ctaAlgo/ctaBacktesting.py @@ -19,6 +19,7 @@ import csv from ctaBase import * from ctaSetting import * +from eventEngine import * from vtConstant import * from vtGateway import VtOrderData, VtTradeData from vtFunction import loadMongoSetting @@ -36,7 +37,7 @@ class BacktestingEngine(object): 2.修改装载数据为批量式后加载模式。 3.增加csv 读取bar的回测模式 4.增加csv 读取tick合并价差的回测模式 - + 5.增加EventEngine,并对newBar增加发送OnBar事件,供外部的回测主体显示Bar线。 """ TICK_MODE = 'tick' # 数据模式,逐Tick回测 @@ -46,8 +47,11 @@ class BacktestingEngine(object): FINAL_MODE = 'Final' # 最后才统计交易,不适合按照百分比等开仓数量计算 #---------------------------------------------------------------------- - def __init__(self): + def __init__(self, eventEngine = None): """Constructor""" + + self.eventEngine = eventEngine + # 本地停止单编号计数 self.stopOrderCount = 0 # stopOrderID = STOPORDERPREFIX + str(stopOrderCount) @@ -698,8 +702,8 @@ class BacktestingEngine(object): leg1BidVolume1 = int(float(row['BidVolume'])) # 排除涨停/跌停的数据 - if (leg1AskPrice1== float('1.79769E308') and leg1AskVolume1 == 0) \ - or (leg1BidPrice1 == float('1.79769E308') and leg1BidVolume1 == 0): + if ((leg1AskPrice1 == float('1.79769E308') or leg1AskPrice1 == 0) and leg1AskVolume1 == 0) \ + or ((leg1BidPrice1 == float('1.79769E308') or leg1BidPrice1 == 0) and leg1BidVolume1 == 0): continue # 叫卖价差=leg1.askPrice1 - leg2.bidPrice1,volume为两者最小 @@ -951,7 +955,15 @@ class BacktestingEngine(object): self.output(u'数据回放结束') - #---------------------------------------------------------------------- + def __sendOnBarEvent(self, bar): + """发送Bar的事件""" + if self.eventEngine is not None: + eventType = EVENT_ON_BAR + '_' + self.symbol + event = Event(type_= eventType) + event.dict_['data'] = bar + self.eventEngine.put(event) + + # ---------------------------------------------------------------------- def newBar(self, bar): """新的K线""" self.bar = bar @@ -959,7 +971,8 @@ class BacktestingEngine(object): self.crossLimitOrder() # 先撮合限价单 self.crossStopOrder() # 再撮合停止单 self.strategy.onBar(bar) # 推送K线到策略中 - + self.__sendOnBarEvent(bar) # 推送K线到事件 + #---------------------------------------------------------------------- def newTick(self, tick): """新的Tick""" @@ -1110,8 +1123,8 @@ class BacktestingEngine(object): # 遍历限价单字典中的所有限价单 for orderID, order in self.workingLimitOrderDict.items(): # 判断是否会成交 - buyCross = order.direction==DIRECTION_LONG and order.price>=buyCrossPrice - sellCross = order.direction==DIRECTION_SHORT and order.price<=sellCrossPrice + buyCross = order.direction==DIRECTION_LONG and order.price >= buyCrossPrice + sellCross = order.direction==DIRECTION_SHORT and order.price <= sellCrossPrice # 如果发生了成交 if buyCross or sellCross: @@ -1159,6 +1172,7 @@ class BacktestingEngine(object): except Exception as ex: self.writeCtaError(u'{0}:{1}'.format(Exception, ex)) + # 实时计算模式 if self.calculateMode == self.REALTIME_MODE: self.realtimeCalculate() @@ -1178,8 +1192,8 @@ class BacktestingEngine(object): # 遍历停止单字典中的所有停止单 for stopOrderID, so in self.workingStopOrderDict.items(): # 判断是否会成交 - buyCross = so.direction==DIRECTION_LONG and so.price<=buyCrossPrice - sellCross = so.direction==DIRECTION_SHORT and so.price>=sellCrossPrice + buyCross = so.direction==DIRECTION_LONG and so.price <= buyCrossPrice + sellCross = so.direction==DIRECTION_SHORT and so.price >= sellCrossPrice # 如果发生了成交 if buyCross or sellCross: @@ -1283,6 +1297,7 @@ class BacktestingEngine(object): longid = EMPTY_STRING shortid = EMPTY_STRING + # 对交易记录逐一处理 for tradeid in self.tradeDict.keys(): trade = self.tradeDict[tradeid] # 多头交易 @@ -1423,8 +1438,8 @@ class BacktestingEngine(object): shortid = tradeid # 当前空头交易为平多 else: - gId = tradeid # 交易组(多个平仓数为一组) s - gr = None # 组合的交易结果 + gId = tradeid # 交易组(多个平仓数为一组) s + gr = None # 组合的交易结果 sellVolume = trade.volume @@ -1577,6 +1592,7 @@ class BacktestingEngine(object): self.writeCtaLog(msg) return + # 对交易结果汇总统计 for time, result in resultDict.items(): if result.pnl > 0: @@ -1662,7 +1678,7 @@ class BacktestingEngine(object): groupId=gId, fixcommission=self.fixCommission) if tv == 0: - if gt==1: + if gt == 1: resultDict[entryTrade.dt] = result else: gr = copy.deepcopy(result) @@ -1832,7 +1848,6 @@ class BacktestingEngine(object): for row in self.exportTradeList: writer.writerow(row) - def getResult(self): # 返回回测结果 d = {} diff --git a/vn.trader/ctaAlgo/ctaBase.py b/vn.trader/ctaAlgo/ctaBase.py index 0f53b700..d1867379 100644 --- a/vn.trader/ctaAlgo/ctaBase.py +++ b/vn.trader/ctaAlgo/ctaBase.py @@ -129,8 +129,8 @@ class CtaTickData(object): #---------------------------------------------------------------------- def __init__(self): """Constructor""" - self.vtSymbol = EMPTY_STRING # vt系统代码 - self.symbol = EMPTY_STRING # 合约代码 + self.vtSymbol = EMPTY_STRING # vt系统代码 CF705 + self.symbol = EMPTY_STRING # 合约代码 CF1705 self.exchange = EMPTY_STRING # 交易所代码 # 成交数据 diff --git a/vn.trader/ctaAlgo/ctaLineBar.py b/vn.trader/ctaAlgo/ctaLineBar.py index 9c680f4e..44669b9f 100644 --- a/vn.trader/ctaAlgo/ctaLineBar.py +++ b/vn.trader/ctaAlgo/ctaLineBar.py @@ -15,6 +15,12 @@ import copy,csv DEBUGCTALOG = True +PERIOD_SECOND = 'second' # 秒级别周期 +PERIOD_MINUTE = 'minute' # 分钟级别周期 +PERIOD_HOUR = 'hour' # 小时级别周期 +PERIOD_DAY = 'day' # 日级别周期 + + class CtaLineBar(object): """CTA K线""" """ 使用方法: @@ -55,6 +61,7 @@ class CtaLineBar(object): # 参数列表 self.paramList.append('barTimeInterval') + self.paramList.append('period') self.paramList.append('inputPreLen') self.paramList.append('inputEma1Len') self.paramList.append('inputEma2Len') @@ -72,6 +79,7 @@ class CtaLineBar(object): self.paramList.append('inputBollLen') self.paramList.append('inputBollStdRate') self.paramList.append('inputKdjLen') + self.paramList.append('inputCciLen') self.paramList.append('inputMacdFastPeriodLen') self.paramList.append('inputMacdSlowPeriodLen') self.paramList.append('inputMacdSignalPeriodLen') @@ -83,9 +91,8 @@ class CtaLineBar(object): # 输入参数 self.name = u'LineBar' - - self.mode = self.TICK_MODE - + self.mode = self.TICK_MODE # 缺省为tick模式 + self.period = PERIOD_SECOND # 缺省为分钟级别周期 self.barTimeInterval = 300 self.barMinuteInterval = self.barTimeInterval / 60 @@ -117,6 +124,7 @@ class CtaLineBar(object): # 当前的Tick self.curTick = None + self.lastTick = None self.curTradingDay = EMPTY_STRING # K 线服务的策略 @@ -132,7 +140,6 @@ class CtaLineBar(object): self.preHigh = [] # K线的前inputPreLen的的最高 self.preLow = [] # K线的前inputPreLen的的最低 - self.lineMa1 = [] # K线的MA1均线,周期是InputMaLen1,不包含当前bar self.lineMa2 = [] # K线的MA2均线,周期是InputMaLen2,不包含当前bar @@ -210,6 +217,9 @@ class CtaLineBar(object): self.lineKdjTop = [] # 记录KDJ最高峰,只保留 inputKdjLen个 self.lineKdjButtom = [] # 记录KDJ的最低谷,只保留 inputKdjLen个 self.lastKdjTopButtom = {} # 最近的一个波峰/波谷 + self.lastK = EMPTY_FLOAT # bar内计算时,最后一个未关闭的bar的实时K值 + self.lastD = EMPTY_FLOAT # bar内计算时,最后一个未关闭的bar的实时值 + self.lastJ = EMPTY_FLOAT # bar内计算时,最后一个未关闭的bar的实时J值 # K线的MACD计算数据 self.inputMacdFastPeriodLen = EMPTY_INT @@ -220,6 +230,10 @@ class CtaLineBar(object): self.lineDea = [] # DEA = (前一日DEA X 8/10 + 今日DIF X 2/10),即为talib-MACD返回值 self.lineMacd = [] # (dif-dea)*2,但是talib中MACD的计算是bar = (dif-dea)*1,国内一般是乘以2 + # K 线的CCI计算数据 + self.inputCciLen = EMPTY_INT + self.lineCci = [] + if setting: self.setParam(setting) @@ -247,12 +261,19 @@ class CtaLineBar(object): if tick.datetime.hour == 8 or tick.datetime.hour == 20: self.writeCtaLog(u'竞价排名tick时间:{0}'.format(tick.datetime)) return + if self.lastTick is None: + self.lastTick = tick self.curTick = tick # 3.生成x K线,若形成新Bar,则触发OnBar事件 self.__drawLineBar(tick) + self.lastTick = tick + + # 4.执行 bar内计算 + self.__recountKdj(countInBar=True) + def addBar(self,bar): """予以外部初始化程序增加bar""" l1 = len(self.lineBar) @@ -268,7 +289,19 @@ class CtaLineBar(object): self.curTradingDay = bar.tradingDay - if abs((bar.datetime - lastBar.datetime).seconds) >= self.barTimeInterval: + if (self.period == PERIOD_SECOND and (bar.datetime-lastBar.datetime).seconds >= self.barTimeInterval) \ + or (self.period == PERIOD_MINUTE and bar.datetime.minute % self.barTimeInterval == 0 + and bar.datetime.minute != lastBar.datetime.minute) \ + or (self.period == PERIOD_HOUR and self.barTimeInterval == 1 and bar.datetime + and bar.datetime.hour != lastBar.datetime.hour) \ + or (self.period == PERIOD_HOUR and self.barTimeInterval == 2 and bar.datetime + and bar.datetime.hour != lastBar.datetime.hour + and bar.datetime.hour in {1, 9, 11, 13, 21, 23}) \ + or (self.period == PERIOD_HOUR and self.barTimeInterval == 4 and bar.datetime + and bar.datetime.hour != lastBar.datetime.hour + and bar.datetime.hour in {1, 9, 13, 21}) \ + or (self.period == PERIOD_DAY and bar.datetime.date != lastBar.datetime.date ): + self.lineBar.append(bar) self.onBar(bar) return @@ -301,6 +334,7 @@ class CtaLineBar(object): self.__recountKdj() self.__recountBoll() self.__recountMacd() + self.__recountCci() # 回调上层调用者 self.onBarFunc(bar) @@ -360,6 +394,9 @@ class CtaLineBar(object): round(self.lineD[-1], 2), round(self.lineJ[-1], 2)) + if self.inputCciLen > 0 and len(self.lineCci) > 0: + msg = msg + u',Cci({0}):{1}'.format(self.inputCciLen, self.lineCci[-1]) + if self.inputBollLen > 0 and len(self.lineUpperBand)>0: msg = msg + u',Boll({0}):u:{1},m:{2},l:{3}'.\ format(self.inputBollLen, round(self.lineUpperBand[-1], 2), @@ -376,7 +413,7 @@ class CtaLineBar(object): self.bar = CtaBarData() # 创建新的K线 # 计算K线的整点分钟周期,这里周期最小是1分钟。如果你是采用非整点分钟,例如1.5分钟,请把这段注解掉 - if self.barMinuteInterval: + if self.barMinuteInterval and self.period == PERIOD_SECOND: self.barMinuteInterval = int(self.barTimeInterval / 60) if self.barMinuteInterval < 1: self.barMinuteInterval = 1 @@ -414,8 +451,6 @@ class CtaLineBar(object): # bar的交易日与记录的当前交易日一致, 交易量为tick的volume,减去上一根bar的日总交易量 self.bar.volume = tick.volume - self.lineBar[-1].dayVolume - - self.barFirstTick = True # 标识该Tick属于该Bar的第一个tick数据 self.lineBar.append(self.bar) # 推入到lineBar队列 @@ -438,62 +473,6 @@ class CtaLineBar(object): # 与最后一个BAR的时间比对,判断是否超过5分钟 lastBar = self.lineBar[-1] - # 专门处理隔夜跳空。隔夜跳空会造成开盘后EMA和ADX的计算错误。 - if len(self.lineAtr2) < 1: - priceInBar = 5 * self.minDiff - else: - priceInBar = self.lineAtr2[-1] - - jumpBars = int(abs(tick.lastPrice - lastBar.close)/priceInBar) - - # 开盘时间 - if (tick.datetime.hour == 9 or tick.datetime.hour == 21) \ - and tick.datetime.minute == 0 and tick.datetime.second == 0 \ - and lastBar.datetime.hour != tick.datetime.hour \ - and jumpBars > 0 and self.activeDayJump: - - priceInYesterday = lastBar.close - - self.writeCtaLog(u'line Bar jumpbars:{0}'.format(jumpBars)) - - if tick.lastPrice > priceInYesterday: # 价格往上跳 - - # 生成砖块递增K线,减小ATR变动 - for i in range(0, jumpBars, 1): - upbar = copy.deepcopy(lastBar) - upbar.open = priceInYesterday + float(i * priceInBar) - upbar.low = upbar.open - upbar.close = priceInYesterday + float((i+1) * priceInBar) - upbar.high = upbar.close - upbar.volume = 0 - - self.lineBar.append(upbar) - self.onBar(upbar) - - else: # 价格往下跳 - # 生成递减K线,减小ATR变动 - for i in range(0, jumpBars, 1): - - downbar = copy.deepcopy(lastBar) - downbar.open = priceInYesterday - float(i * priceInBar) - downbar.high = downbar.open - downbar.close = priceInYesterday - float((i+1) * priceInBar) - downbar.low = downbar.close - downbar.volume = 0 - - self.lineBar.append(downbar) - self.onBar(downbar) - - # 生成平移K线,减小Pdi,Mdi、ADX变动 - for i in range(0, jumpBars*2, 1): - equalbar=copy.deepcopy(self.lineBar[-1]) - equalbar.volume = 0 - self.lineBar.append(equalbar) - self.onBar(equalbar) - - # 重新指定为最后一个Bar - 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) \ @@ -514,8 +493,31 @@ class CtaLineBar(object): if tick.datetime.hour == 23 and tick.datetime.minute == 30: endtick = True - # 满足时间要求,tick的时间,距离最后一个bar的开始时间,已经超出bar的时间周期(barTimeInterval),并且不是最后一个结束tick - if (tick.datetime-lastBar.datetime).seconds >= self.barTimeInterval and not endtick: + # 满足时间要求 + # 1,秒周期,tick的时间,距离最后一个bar的开始时间,已经超出bar的时间周期(barTimeInterval) + # 2,分钟、小时周期,取整=0 + # 3、日周期,开盘时间 + # 4、不是最后一个结束tick + if ((self.period == PERIOD_SECOND and (tick.datetime-lastBar.datetime).seconds >= self.barTimeInterval) \ + or + (self.period == PERIOD_MINUTE and tick.datetime.minute % self.barTimeInterval == 0 + and tick.datetime.minute != self.lastTick.datetime.minute) + or + (self.period == PERIOD_HOUR and self.barTimeInterval == 1 and tick.datetime + and tick.datetime.hour != self.lastTick.datetime.hour) + or + (self.period == PERIOD_HOUR and self.barTimeInterval == 2 and tick.datetime + and tick.datetime.hour != self.lastTick.datetime.hour + and tick.datetime.hour in {1, 9, 11, 13, 21, 23}) + or + (self.period == PERIOD_HOUR and self.barTimeInterval == 4 and tick.datetime + and tick.datetime.hour != self.lastTick.datetime.hour + and tick.datetime.hour in {1, 9, 13, 21}) + or (self.period == PERIOD_DAY and tick.datetime + and (tick.datetime.hour == 21 or tick.datetime.hour == 9) + and 14 <= self.lastTick.datetime.hour <= 15) + ) and not endtick: + # 创建并推入新的Bar self.__firstTick(tick) # 触发OnBar事件 @@ -1136,7 +1138,7 @@ class CtaLineBar(object): self.lastBollLower = l - l % self.minDiff # 下轨取整 - def __recountKdj(self): + def __recountKdj(self, countInBar = False): """KDJ指标""" """ KDJ指标的中文名称又叫随机指标,是一个超买超卖指标,最早起源于期货市场,由乔治·莱恩(George Lane)首创。 @@ -1158,18 +1160,24 @@ class CtaLineBar(object): if self.inputKdjLen <= EMPTY_INT: return if len(self.lineBar) < self.inputKdjLen+1: - self.debugCtaLog(u'数据未充分,当前Bar数据数量:{0},计算KDJ需要:{1}'.format(len(self.lineBar), self.inputKdjLen+1)) + if not countInBar: + self.debugCtaLog(u'数据未充分,当前Bar数据数量:{0},计算KDJ需要:{1}'.format(len(self.lineBar), self.inputKdjLen+1)) return - if self.mode == self.TICK_MODE: + # 数据是Tick模式,非bar内计算 + if self.mode == self.TICK_MODE and not countInBar: listClose =[x.close for x in self.lineBar[-self.inputKdjLen-1:-1]] + listHigh = [x.high for x in self.lineBar[-self.inputKdjLen - 1:-1]] + listLow = [x.low for x in self.lineBar[-self.inputKdjLen - 1:-1]] idx = 2 else: listClose =[x.close for x in self.lineBar[-self.inputKdjLen:]] + listHigh = [x.high for x in self.lineBar[-self.inputKdjLen :]] + listLow = [x.low for x in self.lineBar[-self.inputKdjLen :]] idx = 1 - hhv = max(listClose) - llv = min(listClose) + hhv = max(listHigh) + llv = min(listLow) if len(self.lineK) > 0: lastK = self.lineK[-1] @@ -1184,7 +1192,7 @@ class CtaLineBar(object): if hhv == llv: rsv = 50 else: - rsv= (self.lineBar[-1].close - llv)/(hhv - llv) * 100 + rsv = (self.lineBar[-1].close - llv)/(hhv - llv) * 100 k = 2*lastK/3 + rsv/3 if k < 0: k = 0 @@ -1196,6 +1204,12 @@ class CtaLineBar(object): j = 3*k - 2*d + if countInBar: + self.lastD = d + self.lastK = k + self.lastJ = j + return + if len(self.lineK) > self.inputKdjLen * 8: del self.lineK[0] self.lineK.append(k) @@ -1238,7 +1252,6 @@ class CtaLineBar(object): self.lineKdjButtom.append(b) self.lastKdjTopButtom = self.lineKdjButtom[-1] - def __recountMacd(self): """ Macd计算方法: @@ -1258,7 +1271,7 @@ class CtaLineBar(object): maxLen = max(self.inputMacdFastPeriodLen,self.inputMacdSlowPeriodLen)+self.inputMacdSignalPeriodLen+1 - maxLen = maxLen * 3 # 注:数据长度需要足够,才能准确。测试过,3倍长度才可以与国内的文华等软件一致 + #maxLen = maxLen * 3 # 注:数据长度需要足够,才能准确。测试过,3倍长度才可以与国内的文华等软件一致 if len(self.lineBar) < maxLen: self.debugCtaLog(u'数据未充分,当前Bar数据数量:{0},计算MACD需要:{1}'.format(len(self.lineBar), maxLen)) @@ -1289,6 +1302,56 @@ class CtaLineBar(object): del self.lineMacd[0] self.lineMacd.append(macd[-1]*2) # 国内一般是2倍 + def __recountCci(self): + """CCI计算 + 顺势指标又叫CCI指标,CCI指标是美国股市技术分析 家唐纳德·蓝伯特(Donald Lambert)于20世纪80年代提出的,专门测量股价、外汇或者贵金属交易 + 是否已超出常态分布范围。属于超买超卖类指标中较特殊的一种。波动于正无穷大和负无穷大之间。但是,又不需要以0为中轴线,这一点也和波动于正无穷大 + 和负无穷大的指标不同。 + 它最早是用于期货市场的判断,后运用于股票市场的研判,并被广泛使用。与大多数单一利用股票的收盘价、开盘价、最高价或最低价而发明出的各种技术分析 + 指标不同,CCI指标是根据统计学原理,引进价格与固定期间的股价平均区间的偏离程度的概念,强调股价平均绝对偏差在股市技术分析中的重要性,是一种比 + 较独特的技术指标。 + 它与其他超买超卖型指标又有自己比较独特之处。象KDJ、W%R等大多数超买超卖型指标都有“0-100”上下界限,因此,它们对待一般常态行情的研判比较适用 + ,而对于那些短期内暴涨暴跌的股票的价格走势时,就可能会发生指标钝化的现象。而CCI指标却是波动于正无穷大到负无穷大之间,因此不会出现指标钝化现 + 象,这样就有利于投资者更好地研判行情,特别是那些短期内暴涨暴跌的非常态行情。 + http://baike.baidu.com/view/53690.htm?fromtitle=CCI%E6%8C%87%E6%A0%87&fromid=4316895&type=syn + + + """ + if self.inputCciLen <= 0: + return + + # 1、lineBar满足长度才执行计算 + if len(self.lineBar) < self.inputCciLen+2: + self.debugCtaLog(u'数据未充分,当前Bar数据数量:{0},计算CCI需要:{1}'. + format(len(self.lineBar), self.inputCciLen + 2)) + return + + # 计算第1根RSI曲线 + + # 3、inputCc1Len(包含当前周期) + if self.mode == self.TICK_MODE: + listClose = [x.close for x in self.lineBar[-self.inputCciLen - 2:-1]] + listHigh = [x.high for x in self.lineBar[-self.inputCciLen - 2:-1]] + listLow = [x.low for x in self.lineBar[-self.inputCciLen - 2:-1]] + idx = 2 + else: + listClose = [x.close for x in self.lineBar[-self.inputCciLen-1:]] + listHigh = [x.high for x in self.lineBar[-self.inputCciLen - 1:]] + listLow = [x.low for x in self.lineBar[-self.inputCciLen - 1:]] + idx = 1 + + barCci = ta.CCI(high=numpy.array(listHigh, dtype=float), low=numpy.array(listLow, dtype=float), + close=numpy.array(listClose, dtype=float), timeperiod=self.inputCciLen)[-1] + + barCci = round(float(barCci), 3) + + l = len(self.lineCci) + if l > self.inputCciLen*8: + del self.lineCci[0] + + self.lineCci.append(barCci) + + # ---------------------------------------------------------------------- def writeCtaLog(self, content): """记录CTA日志""" diff --git a/vn.trader/ctaAlgo/ctaPosition.py b/vn.trader/ctaAlgo/ctaPosition.py index b3d5269c..58263615 100644 --- a/vn.trader/ctaAlgo/ctaPosition.py +++ b/vn.trader/ctaAlgo/ctaPosition.py @@ -18,29 +18,22 @@ class CtaPosition: self.strategy = strategy self.pos = 0 # 持仓状态 0:空仓 >=1 多仓 <=-1 空仓 - self.maxPos = 1 # 最大持仓量 - self.step = 1 # 增仓数量 self.posList = [] - - self.avgPrice = EMPTY_FLOAT def avaliablePos2Add(self): """剩余可加的仓位数量""" - return self.maxPos - abs(self.pos) def openPos(self, direction, vol, price = EMPTY_FLOAT): """开、加仓""" - if self.pos == 0: self.posList = [] if direction == DIRECTION_LONG: # 加多仓 - if self.pos + vol > self.maxPos: self.writeCtaLog(u'异常,超出仓位,当前仓位:{0},加仓:{1},最大仓位:{2}'.format(self.pos,vol,self.maxPos)) return False @@ -49,9 +42,7 @@ class CtaPosition: self.pos = self.pos + vol self.strategy.pos = self.pos - if direction == DIRECTION_SHORT: # 加空仓 - if self.pos - vol < 0 - self.maxPos: self.writeCtaLog(u'异常,超出仓位,当前仓位:{0},加仓:{1},最大仓位:{2}'.format(self.pos, vol, self.maxPos)) return False diff --git a/vn.trader/ctaAlgo/ctaSetting.py b/vn.trader/ctaAlgo/ctaSetting.py index 448a43b9..34c6b4aa 100644 --- a/vn.trader/ctaAlgo/ctaSetting.py +++ b/vn.trader/ctaAlgo/ctaSetting.py @@ -13,10 +13,14 @@ from strategy22_ArbitrageGrid import Strategy22 from strategy24_M15RB import Strategy24 from strategy25_NonStdArbitrageGrid import Strategy25 +from strategy26_ArbitrageM1 import Strategy26 +from strategy27_MultiPeriod import Strategy27 STRATEGY_CLASS = {} #STRATEGY_CLASS['DataRecorder'] = DataRecorder #STRATEGY_CLASS['DoubleEmaDemo'] = DoubleEmaDemo STRATEGY_CLASS['Strategy22'] = Strategy22 STRATEGY_CLASS['Strategy24'] = Strategy24 -STRATEGY_CLASS['Strategy25'] = Strategy25 \ No newline at end of file +STRATEGY_CLASS['Strategy25'] = Strategy25 +STRATEGY_CLASS['Strategy26'] = Strategy26 +STRATEGY_CLASS['Strategy27'] = Strategy27 \ No newline at end of file diff --git a/vn.trader/ctaAlgo/sinaClient.py b/vn.trader/ctaAlgo/sinaClient.py deleted file mode 100644 index 35fbf77f..00000000 --- a/vn.trader/ctaAlgo/sinaClient.py +++ /dev/null @@ -1,214 +0,0 @@ -# encoding: UTF-8 - -from __future__ import print_function - -'''一个简单的SINA数据客户端,主要使用requests开发''' -import requests -from time import sleep -import execjs -from datetime import datetime,timedelta -from ctaBase import CtaBarData,CtaTickData - -class SinaClient(object): - - # ---------------------------------------------------------------------- - def __init__(self, strategy): - - self.strategy = strategy - - requests.adapters.DEFAULT_RETRIES = 5 - self.session = requests.session() - self.session.keep_alive = False - - def getTicks(self, symbol, callback): - - # 从sina加载最新的M1数据 - try: - - url = u'http://stock2.finance.sina.com.cn/futures/api/json.php/InnerFuturesService.getInnerFutures5MLine?symbol={0}'.format( - symbol) - self.strategy.writeCtaLog(u'从sina下载{0}Tick数据 {1}'.format(symbol, url)) - responses = execjs.eval(self.session.get(url).content.decode('gbk').split('\n')[-1]) - - datevalue = datetime.now().strftime('%Y-%m-%d') - - for j, day_item in enumerate(responses[str(symbol).upper()]): - for i, item in enumerate(day_item): - - tick = CtaTickData() - tick.vtSymbol = symbol - tick.symbol = symbol - - if len(item) >= 6: - datevalue = item[6] - - tick.date = datevalue - tick.time = item[4] + u':00' - tick.datetime = datetime.strptime(tick.date + ' ' + tick.time, '%Y-%m-%d %H:%M:%S') - - tick.lastPrice = float(item[0]) - tick.volume = int(item[2]) - - if type(item[3]) == type(None): - tick.openInterest = 0 - else: - tick.openInterest = int(item[3]) - - callback(tick) - - return True - - except Exception as e: - self.strategy.writeCtaLog(u'加载sina历史Tick数据失败:' + str(e)) - return False - - - def getMinBars(self, symbol, minute, callback): - """# 从sina加载最新的M5,M15,M30,M60数据""" - - if minute not in {5, 15, 30, 60}: - return False - - sinaBars = [] - try: - - url = u'http://stock2.finance.sina.com.cn/futures/api/json.php/InnerFuturesService.getInnerFutures{0}MinKLine?symbol={1}'.format(minute,symbol) - self.strategy.writeCtaLog(u'从sina下载{0}的{1}分钟数据 {2}'.format(symbol,minute, url)) - responses = execjs.eval(self.session.get(url).content.decode('gbk').split('\n')[-1]) - dayVolume = 0 - - for item in responses: - bar = CtaBarData() - - bar.vtSymbol = symbol - bar.symbol = symbol - # bar的close time - sinaDt = datetime.strptime(item[0], '%Y-%m-%d %H:%M:00') - - if minute in {5, 15} and sinaDt.hour == 10 and sinaDt.minute == 30: - # 这个是sina的bug,它把10:15 ~10:30也包含进来了 - continue - - if minute == 60 and sinaDt.hour in {11,23,1,2} and sinaDt.minute == 30: - bar.datetime = sinaDt - timedelta(seconds=(minute /2)* 60) - else: - bar.datetime = sinaDt - timedelta(seconds=minute * 60) - - bar.date = bar.datetime.strftime('%Y%m%d') - bar.tradingDay = bar.date # todo: 需要修改,晚上21点后,修改为next workingday - bar.time = bar.datetime.strftime('%H:%M:00') - - bar.open = float(item[1]) - bar.high = float(item[2]) - bar.low = float(item[3]) - bar.close = float(item[4]) - bar.volume = int(item[5]) - - # 计算dayvolume - if not sinaBars: - dayVolume = bar.volume - else: - if sinaBars[-1].datetime.hour == 14 and bar.datetime.hour !=14: - dayVolume = bar.volume - else: - dayVolume += bar.volume - - bar.dayVolume = dayVolume - - sinaBars.append(bar) - - if len(sinaBars)>0: - self.strategy.writeCtaLog(u'从sina读取了{0}条{1}分钟数据'.format(len(sinaBars),minute)) - - # 把sina的bar灌入回调函数 - for bar in sinaBars: - callback(bar) - - # 处理完毕,清空 - sinaBars = [] - - return True - else: - self.strategy.writeCtaLog(u'从sina读取{0}分钟数据失败'.format(minute)) - return False - - except Exception as e: - self.strategy.writeCtaLog(u'加载Sina历史分钟数据失败:'+str(e)) - return False - - def getDayBars(self, symbol, callback): - """# 从sina加载最新的Day数据""" - - sinaBars = [] - - try: - - url = u'http://stock.finance.sina.com.cn/futures/api/json.php/InnerFuturesService.getInnerFuturesDailyKLine?symbol={0}'.format(symbol) - self.strategy.writeCtaLog(u'从sina下载{0}的日K数据 {1}'.format(symbol, url)) - responses = execjs.eval(self.session.get(url).content.decode('gbk')) - dayVolume = 0 - - for item in responses: - bar = CtaBarData() - - bar.vtSymbol = symbol - bar.symbol = symbol - # bar的close time - bar.datetime = datetime.strptime(item['date'], '%Y-%m-%d') - bar.date = bar.datetime.strftime('%Y%m%d') - bar.tradingDay = bar.date # todo: 需要修改,晚上21点后,修改为next workingday - bar.time = bar.datetime.strftime('%H:%M:00') - - bar.open = float(item['open']) - bar.high = float(item['high']) - bar.low = float(item['low']) - bar.close = float(item['close']) - bar.volume = int(item['volume']) - bar.dayVolume = bar.volume - - sinaBars.append(bar) - - if len(sinaBars)>0: - self.strategy.writeCtaLog(u'从sina读取了{0}条日线K数据'.format(len(sinaBars))) - - # 把sina的bar灌入回调函数 - for bar in sinaBars: - callback(bar) - - # 处理完毕,清空 - sinaBars = [] - - return True - else: - self.strategy.writeCtaLog(u'从sina读取日线K数据失败') - return False - - except Exception as e: - self.strategy.writeCtaLog(u'加载Sina历史日线数据失败:'+str(e)) - return False - -class TestStrategy(object): - - def __init__(self): - pass - - def addBar(self, bar): - print(u'{0},o:{1},h:{2},l:{3},c:{4},v:{5}'.format(bar.datetime, bar.open, bar.high, bar.low, bar.close, bar.volume)) - - def addTick(self, tick): - print(u'{0},{1},ap:{2},av:{3},bp:{4},bv:{5}'.format(tick.datetime, tick.lastPrice, tick.askPrice1, tick.askVolume1, tick.bidPrice1, tick.bidVolume1)) - - def writeCtaLog(self, content): - print(content) - -if __name__ == '__main__': - t = TestStrategy() - - sina = SinaClient(t) - - #rt=sina.getDayBars(symbol='RB1705', callback=t.addBar) - - #rt = sina.getMinBars(symbol='RB1705',minute = 5, callback=t.addBar) - - rt = sina.getTicks(symbol='RB1705', callback=t.addTick) - diff --git a/vn.trader/ctaAlgo/uiCtaWidget.py b/vn.trader/ctaAlgo/uiCtaWidget.py index 17065e50..216ebd82 100644 --- a/vn.trader/ctaAlgo/uiCtaWidget.py +++ b/vn.trader/ctaAlgo/uiCtaWidget.py @@ -4,6 +4,8 @@ CTA模块相关的GUI控制组件 ''' +import sys +sys.path.append('..') from uiBasicWidget import QtGui, QtCore, BasicCell from eventEngine import * @@ -19,7 +21,7 @@ class CtaValueMonitor(QtGui.QTableWidget): def __init__(self, parent=None): """Constructor""" super(CtaValueMonitor, self).__init__(parent) - + self.keyCellDict = {} self.data = None self.inited = False @@ -39,9 +41,12 @@ class CtaValueMonitor(QtGui.QTableWidget): def updateData(self, data): """更新数据""" if not self.inited: + # 设置标题 + self.setColumnCount(len(data)) self.setHorizontalHeaderLabels(data.keys()) - + + # 新增数据 col = 0 for k, v in data.items(): cell = QtGui.QTableWidgetItem(unicode(v)) @@ -51,10 +56,14 @@ class CtaValueMonitor(QtGui.QTableWidget): self.inited = True else: + # 更新数据 for k, v in data.items(): cell = self.keyCellDict[k] cell.setText(unicode(v)) + #cell.setBackgroundColor() + + # 调整表格宽度为自适应 self.resizeColumnsToContents() self.resizeRowsToContents()