From d20e89c2eb2ceffcb47b23a598c09ee16f16500d Mon Sep 17 00:00:00 2001 From: WOLF Date: Tue, 26 May 2015 16:05:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=BC=94=E7=A4=BA=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E5=8F=8C=E6=8C=87=E6=95=B0=E5=9D=87=E7=BA=BF=E7=AD=96?= =?UTF-8?q?=E7=95=A5Demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vn.strategy/README.md | 8 +- vn.strategy/strategyEngine.py | 84 +- vn.strategy/strategydemo/demoEngine.py | 2 +- vn.strategy/strategydemo/demoMain.py | 32 - vn.strategy/strategydemo/demoStrategy.py | 292 ++++- vn.strategy/strategydemo/demoUi.py | 1269 -------------------- vn.strategy/strategydemo/strategyEngine.py | 55 +- 7 files changed, 388 insertions(+), 1354 deletions(-) delete mode 100644 vn.strategy/strategydemo/demoMain.py delete mode 100644 vn.strategy/strategydemo/demoUi.py diff --git a/vn.strategy/README.md b/vn.strategy/README.md index 82216116..c7d7d2e6 100644 --- a/vn.strategy/README.md +++ b/vn.strategy/README.md @@ -1,11 +1,9 @@ #vn.strategy介绍 -##2015/5/21 - -该模块主要包含了一个适用于CTA类策略(时间序列型)的策略引擎和策略模板。 - -目前尚未完成,仅供参考。 +##2015/5/26 +策略引擎目前主要对接测试用的是CTP,LTS的理论上只要稍作修改就可以直接用。 +完成了一个演示性的双指数均线策略,填入账号、密码等信息后直接运行demoStrategy.py就可以启动。 diff --git a/vn.strategy/strategyEngine.py b/vn.strategy/strategyEngine.py index 29bda713..78c778c2 100644 --- a/vn.strategy/strategyEngine.py +++ b/vn.strategy/strategyEngine.py @@ -174,13 +174,20 @@ class StrategyEngine(object): # 调用函数 self.__connectMongo() - self.createStrategy() self.__registerEvent() #---------------------------------------------------------------------- - def createStrategy(self): + def createStrategy(self, strategyName, strategySymbol, strategyClass, strategySetting): """创建策略""" - pass + strategy = strategyClass(strategyName, strategySymbol, self) + self.dictStrategy[strategyName] = strategy + strategy.loadSetting(strategySetting) + + # 订阅合约行情,注意这里因为是CTP,所以ExchangeID可以忽略 + self.mainEngine.subscribe(strategySymbol, None) + + # 注册策略监听 + self.registerStrategy(strategySymbol, strategy) #---------------------------------------------------------------------- def __connectMongo(self): @@ -331,7 +338,7 @@ class StrategyEngine(object): order.orderRef = data['OrderRef'] order.direction = data['Direction'] - order.offset = data['ComboOffsetFlag'] + order.offset = data['CombOffsetFlag'] order.price = data['LimitPrice'] order.volumeOriginal = data['VolumeTotalOriginal'] @@ -353,8 +360,10 @@ class StrategyEngine(object): #---------------------------------------------------------------------- def __updateTrade(self, event): """成交更新""" + print 'updateTrade' data = event.dict_['data'] orderRef = data['OrderRef'] + print 'trade:', orderRef if orderRef in self.__dictOrderRefStrategy: @@ -396,6 +405,8 @@ class StrategyEngine(object): offset) self.__dictOrderRefStrategy[ref] = strategy + print 'ref:', ref + print 'strategy:', strategy.name return ref @@ -480,17 +491,29 @@ class StrategyEngine(object): except KeyError: pass + #---------------------------------------------------------------------- + def startAll(self): + """启动所有策略""" + for strategy in self.dictStrategy.values(): + strategy.start() + + #---------------------------------------------------------------------- + def stopAll(self): + """停止所有策略""" + for strategy in self.dictStrategy.values(): + strategy.stop() + ######################################################################## -class SimpleStrategyTemplate(object): - """简易策略模板""" +class StrategyTemplate(object): + """策略模板""" #---------------------------------------------------------------------- def __init__(self, name, symbol, engine): """Constructor""" self.name = name # 策略名称(注意唯一性) - self.__engine = engine # 策略引擎对象 self.symbol = symbol # 策略交易的合约 + self.engine = engine # 策略引擎对象 self.trading = False # 策略是否启动交易 @@ -513,6 +536,11 @@ class SimpleStrategyTemplate(object): def onStopOrder(self, orderRef): """停止单更新""" raise NotImplementedError + + #---------------------------------------------------------------------- + def onBar(self, o, h, l, c, volume, time): + """K线数据更新""" + raise NotImplementedError #---------------------------------------------------------------------- def start(self): @@ -522,6 +550,7 @@ class SimpleStrategyTemplate(object): 有需要可以重新实现更复杂的操作 """ self.trading = True + self.engine.writeLog(self.name + u'开始运行') #---------------------------------------------------------------------- def stop(self): @@ -530,75 +559,84 @@ class SimpleStrategyTemplate(object): 同上 """ self.trading = False + self.engine.writeLog(self.name + u'停止运行') #---------------------------------------------------------------------- - def __buy(self, price, volume, stopOrder=False): + def loadSetting(self, setting): + """ + 载入设置 + setting通常是一个包含了参数设置的字典 + """ + raise NotImplementedError + + #---------------------------------------------------------------------- + def buy(self, price, volume, stopOrder=False): """买入开仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_BUY, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY, OFFSET_OPEN, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_BUY, + ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY, OFFSET_OPEN, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __cover(self, price, volume, StopOrder=False): + def cover(self, price, volume, StopOrder=False): """买入平仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_BUY, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY, OFFSET_CLOSE, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_BUY, + ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY, OFFSET_CLOSE, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __sell(self, price, volume, stopOrder=False): + def sell(self, price, volume, stopOrder=False): """卖出平仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_SELL, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL, OFFSET_CLOSE, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_SELL, + ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL, OFFSET_CLOSE, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __short(self, price, volume, stopOrder=False): + def short(self, price, volume, stopOrder=False): """卖出开仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_SELL, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL, OFFSET_OPEN, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_SELL, + ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL, OFFSET_OPEN, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __cancelOrder(self, orderRef): + def cancelOrder(self, orderRef): """撤单""" - self.__engine.cancelOrder(orderRef) + self.engine.cancelOrder(orderRef) #---------------------------------------------------------------------- - def __cancelStopOrder(self, so): + def cancelStopOrder(self, so): """撤销停止单""" - self.__engine.cancelStopOrder(so) + self.engine.cancelStopOrder(so) \ No newline at end of file diff --git a/vn.strategy/strategydemo/demoEngine.py b/vn.strategy/strategydemo/demoEngine.py index 6cd4a603..25f28592 100644 --- a/vn.strategy/strategydemo/demoEngine.py +++ b/vn.strategy/strategydemo/demoEngine.py @@ -82,7 +82,7 @@ class MainEngine: def sendOrder(self, instrumentid, exchangeid, price, pricetype, volume, direction, offset): """发单""" ref = self.td.sendOrder(instrumentid, exchangeid, price, pricetype, volume, direction, offset) - return ref + return str(ref) #---------------------------------------------------------------------- def cancelOrder(self, instrumentid, exchangeid, orderref, frontid, sessionid): diff --git a/vn.strategy/strategydemo/demoMain.py b/vn.strategy/strategydemo/demoMain.py deleted file mode 100644 index fe5ab501..00000000 --- a/vn.strategy/strategydemo/demoMain.py +++ /dev/null @@ -1,32 +0,0 @@ -# encoding: UTF-8 - -""" -该文件中包含的是交易平台的主函数, -将底层、中层、上层的功能导入,并运行。 -""" - -import ctypes -import sys - -from demoEngine import MainEngine -from demoUi import * - -#---------------------------------------------------------------------- -def main(): - """主程序入口""" - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('vn.py demo') # win7以下请注释掉该行 - - app = QtGui.QApplication(sys.argv) - app.setWindowIcon(QtGui.QIcon('vnpy.ico')) - - me = MainEngine() - - mw = MainWindow(me.ee, me) - mw.showMaximized() - - sys.exit(app.exec_()) - - -if __name__ == '__main__': - main() - diff --git a/vn.strategy/strategydemo/demoStrategy.py b/vn.strategy/strategydemo/demoStrategy.py index dc09763e..d2d8fba4 100644 --- a/vn.strategy/strategydemo/demoStrategy.py +++ b/vn.strategy/strategydemo/demoStrategy.py @@ -1,18 +1,306 @@ # encoding: UTF-8 -from strategyEngine import StrategyTemplate +# 首先写系统内置模块 +import sys +from datetime import datetime, timedelta, time +from time import sleep + +# 然后是第三方库模块(如PyQt4等) +import sip +from PyQt4 import QtCore + +# 然后是自己编写的模块 +from demoEngine import MainEngine +from strategyEngine import * + ######################################################################## class SimpleEmaStrategy(StrategyTemplate): - """简单双指数移动均线策略""" + """简单双指数移动均线EMA策略""" #---------------------------------------------------------------------- def __init__(self, name, symbol, engine): """Constructor""" super(SimpleEmaStrategy, self).__init__(name, symbol, engine) + # 策略在外部设置的参数 + self.fastAlpha = 0.2 # 快速EMA的参数 + self.slowAlpha = 0.05 # 慢速EMA的参数 + # 最新TICK数据(市场报价) + self.currentTick = None + # K线缓存对象 + self.barOpen = 0 + self.barHigh = 0 + self.barLow = 0 + self.barClose = 0 + self.barVolume = 0 + self.barTime = None + + # 保存K线数据的列表对象 + self.listOpen = [] + self.listHigh = [] + self.listLow = [] + self.listClose = [] + self.listVolume = [] + self.listTime = [] + + # 持仓 + self.pos = 0 + + # 报单代码列表 + self.listOrderRef = [] # 报单号列表 + self.listStopOrder = [] # 停止单对象列表 + + # EMA均线 + self.fastEMA = 0 # 快速EMA的数值 + self.slowEMA = 0 # 慢速EMA的数值 + + # 是否完成了初始化 + self.initCompleted = False + + # 初始化时读取的历史数据的起始日期(可以选择外部设置) + self.startDate = None + + #---------------------------------------------------------------------- + def loadSetting(self, setting): + """读取参数设定""" + try: + self.fastAlpha = setting['fastAlpha'] + self.slowAlpha = setting['slowAlpha'] + self.engine.writeLog(self.name + u'读取参数成功') + except KeyError: + self.engine.writeLog(self.name + u'读取参数设定出错,请检查参数字典') + + self.initStrategy() + + #---------------------------------------------------------------------- + def initStrategy(self): + """初始化""" + td = timedelta(days=3) # 读取3天的历史TICK数据 + today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) + cx = self.engine.loadTick(self.symbol, today-td) + + if cx: + for data in cx: + tick = Tick(data['InstrumentID']) + + tick.openPrice = data['OpenPrice'] + tick.highPrice = data['HighestPrice'] + tick.lowPrice = data['LowestPrice'] + tick.lastPrice = data['LastPrice'] + + tick.volume = data['Volume'] + tick.openInterest = data['OpenInterest'] + + tick.upperLimit = data['UpperLimitPrice'] + tick.lowerLimit = data['LowerLimitPrice'] + + tick.time = data['UpdateTime'] + tick.ms = data['UpdateMillisec'] + + tick.bidPrice1 = data['BidPrice1'] + tick.bidPrice2 = data['BidPrice2'] + tick.bidPrice3 = data['BidPrice3'] + tick.bidPrice4 = data['BidPrice4'] + tick.bidPrice5 = data['BidPrice5'] + + tick.askPrice1 = data['AskPrice1'] + tick.askPrice2 = data['AskPrice2'] + tick.askPrice3 = data['AskPrice3'] + tick.askPrice4 = data['AskPrice4'] + tick.askPrice5 = data['AskPrice5'] + + tick.bidVolume1 = data['BidVolume1'] + tick.bidVolume2 = data['BidVolume2'] + tick.bidVolume3 = data['BidVolume3'] + tick.bidVolume4 = data['BidVolume4'] + tick.bidVolume5 = data['BidVolume5'] + + tick.askVolume1 = data['AskVolume1'] + tick.askVolume2 = data['AskVolume2'] + tick.askVolume3 = data['AskVolume3'] + tick.askVolume4 = data['AskVolume4'] + tick.askVolume5 = data['AskVolume5'] + + self.onTick(tick) + + self.initCompleted = True + + self.engine.writeLog(self.name + u'初始化完成') + + #---------------------------------------------------------------------- + def onTick(self, tick): + """行情更新""" + # 保存最新的TICK + self.currentTick = tick + + # 首先生成datetime.time格式的时间(便于比较) + ticktime = self.strToTime(tick.time, tick.ms) + + # 假设是收到的第一个TICK + if self.barOpen == 0: + # 初始化新的K线数据 + self.barOpen = tick.lastPrice + self.barHigh = tick.lastPrice + self.barLow = tick.lastPrice + self.barClose = tick.lastPrice + self.barVolume = tick.volume + self.barTime = ticktime + else: + # 如果是当前一分钟内的数据 + if ticktime.minute == self.barTime.minute: + # 汇总TICK生成K线 + self.barHigh = max(self.barHigh, tick.lastPrice) + self.barLow = min(self.barLow, tick.lastPrice) + self.barClose = tick.lastPrice + self.barVolume = self.barVolume + tick.volume + self.barTime = ticktime + # 如果是新一分钟的数据 + else: + # 首先推送K线数据 + self.onBar(self.barOpen, self.barHigh, self.barLow, self.barClose, + self.barVolume, self.barTime) + + # 初始化新的K线数据 + self.barOpen = tick.lastPrice + self.barHigh = tick.lastPrice + self.barLow = tick.lastPrice + self.barClose = tick.lastPrice + self.barVolume = tick.volume + self.barTime = ticktime + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """交易更新""" + if trade.direction == DIRECTION_BUY: + self.pos = self.pos + trade.volume + else: + self.pos = self.pos - trade.volume + + log = self.name + u'当前持仓:' + str(self.pos) + self.engine.writeLog(log) + + #---------------------------------------------------------------------- + def onOrder(self, order): + """报单更新""" + pass + + #---------------------------------------------------------------------- + def onStopOrder(self, orderRef): + """停止单更新""" + pass + + #---------------------------------------------------------------------- + def onBar(self, o, h, l, c, volume, time): + """K线数据更新""" + # 保存K线序列数据 + self.listOpen.append(o) + self.listHigh.append(h) + self.listLow.append(l) + self.listClose.append(c) + self.listVolume.append(volume) + self.listTime.append(time) + + # 计算EMA + if self.fastEMA: + self.fastEMA = c*self.fastAlpha + self.fastEMA*(1-self.fastAlpha) + self.slowEMA = c*self.slowAlpha + self.slowEMA*(1-self.slowAlpha) + else: + self.fastEMA = c + self.slowEMA = c + + # 交易逻辑 + if self.initCompleted: # 首先检查是否是实盘运行还是数据预处理阶段 + # 快速EMA在慢速EMA上方,做多 + if self.fastEMA > self.slowEMA: + # 如果当前手头无仓位,则直接做多 + if self.pos == 0: + # 涨停价买入开仓 + self.buy(self.currentTick.upperLimit, 1) + # 手头有空仓,则先平空,再开多 + elif self.pos < 0: + self.cover(self.currentTick.upperLimit, 1) + self.buy(self.currentTick.upperLimit, 1) + + # 反之,做空 + elif self.fastEMA < self.slowEMA: + if self.pos == 0: + self.short(self.currentTick.lowerLimit, 1) + elif self.pos > 0: + self.sell(self.currentTick.lowerLimit, 1) + self.short(self.currentTick.lowerLimit, 1) + + # 记录日志 + log = self.name + u'当前时间:' + str(time) + \ + u',快速EMA:' + str(self.fastEMA) + u',慢速EMA:' + \ + str(self.slowEMA) + self.engine.writeLog(log) + + print 'onBar', o, h, l, c, time + print 'fastEMA:', self.fastEMA, '|slowEMA:', self.slowEMA + + #---------------------------------------------------------------------- + def strToTime(self, t, ms): + """从字符串时间转化为time格式的时间""" + hh, mm, ss = t.split(':') + tt = time(int(hh), int(mm), int(ss), microsecond=ms) + return tt + +#---------------------------------------------------------------------- +def print_log(event): + """打印日志""" + log = event.dict_['log'] + print str(datetime.now()), ':', log + + +#---------------------------------------------------------------------- +def main(): + """运行在CMD中的演示程度""" + # 创建PyQt4应用对象 + app = QtCore.QCoreApplication(sys.argv) + + # 创建主引擎对象 + me = MainEngine() + + # 注册事件监听 + me.ee.register(EVENT_LOG, print_log) + + # 登录 + userid = '' + password = '' + brokerid = '' + mdAddress = '' + tdAddress = '' + + me.login(userid, password, brokerid, mdAddress, tdAddress) + + # 等待10秒钟(初始化合约数据等) + sleep(10) + + # 创建策略引擎对象 + se = StrategyEngine(me.ee, me) + + # 创建策略对象 + setting = {} + setting['fastAlpha'] = 0.2 + setting['slowAlpha'] = 0.05 + se.createStrategy(u'EMA演示策略', 'IF1506', SimpleEmaStrategy, setting) + + # 启动所有策略 + se.startAll() + + # 让程序连续运行 + sys.exit(app.exec_()) + + +if __name__ == '__main__': + main() + + + + \ No newline at end of file diff --git a/vn.strategy/strategydemo/demoUi.py b/vn.strategy/strategydemo/demoUi.py deleted file mode 100644 index d972fa97..00000000 --- a/vn.strategy/strategydemo/demoUi.py +++ /dev/null @@ -1,1269 +0,0 @@ -# encoding: UTF-8 - -""" -该文件中包含的是交易平台的上层UI部分, -通过图形界面调用中间层的主动函数,并监控相关数据更新。 - -Monitor主要负责监控数据,有部分包含主动功能。 -Widget主要用于调用主动功能,有部分包含数据监控。 -""" - -from __future__ import division - -import time -import sys -import shelve -from collections import OrderedDict - -import sip -from PyQt4 import QtCore, QtGui - -from eventEngine import * - - - -######################################################################## -class LogMonitor(QtGui.QTableWidget): - """用于显示日志""" - signal = QtCore.pyqtSignal(type(Event())) - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, parent=None): - """Constructor""" - super(LogMonitor, self).__init__(parent) - self.__eventEngine = eventEngine - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """初始化界面""" - self.setWindowTitle(u'日志') - - self.setColumnCount(2) - self.setHorizontalHeaderLabels([u'时间', u'日志']) - - self.verticalHeader().setVisible(False) # 关闭左边的垂直表头 - self.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) # 设为不可编辑状态 - - # 自动调整列宽 - self.horizontalHeader().setResizeMode(0, QtGui.QHeaderView.ResizeToContents) - self.horizontalHeader().setResizeMode(1, QtGui.QHeaderView.Stretch) - - #---------------------------------------------------------------------- - def registerEvent(self): - """注册事件监听""" - # Qt图形组件的GUI更新必须使用Signal/Slot机制,否则有可能导致程序崩溃 - # 因此这里先将图形更新函数作为Slot,和信号连接起来 - # 然后将信号的触发函数注册到事件驱动引擎中 - self.signal.connect(self.updateLog) - self.__eventEngine.register(EVENT_LOG, self.signal.emit) - - #---------------------------------------------------------------------- - def updateLog(self, event): - """更新日志""" - # 获取当前时间和日志内容 - t = time.strftime('%H:%M:%S',time.localtime(time.time())) - log = event.dict_['log'] - - # 在表格最上方插入一行 - self.insertRow(0) - - # 创建单元格 - cellTime = QtGui.QTableWidgetItem(t) - cellLog = QtGui.QTableWidgetItem(log) - - # 将单元格插入表格 - self.setItem(0, 0, cellTime) - self.setItem(0, 1, cellLog) - - -######################################################################## -class AccountMonitor(QtGui.QTableWidget): - """用于显示账户""" - signal = QtCore.pyqtSignal(type(Event())) - - dictLabels = OrderedDict() - dictLabels['AccountID'] = u'投资者账户' - dictLabels['PreBalance'] = u'昨结' - dictLabels['Withdraw'] = u'出金' - dictLabels['Deposit'] = u'入金' - dictLabels['FrozenCash'] = u'冻结资金' - dictLabels['FrozenMargin'] = u'冻结保证金' - dictLabels['FrozenCommission'] = u'冻结手续费' - dictLabels['Commission'] = u'手续费' - dictLabels['CurrMargin'] = u'当前保证金' - dictLabels['Available'] = u'可用资金' - dictLabels['WithdrawQuota'] = u'可取资金' - dictLabels['Balance'] = u'账户资金' - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, parent=None): - """Constructor""" - super(AccountMonitor, self).__init__(parent) - self.__eventEngine = eventEngine - - self.dictAccount = {} # 用来保存账户对应的单元格 - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - self.setWindowTitle(u'账户') - - self.setColumnCount(len(self.dictLabels)) - self.setHorizontalHeaderLabels(self.dictLabels.values()) - - self.verticalHeader().setVisible(False) # 关闭左边的垂直表头 - self.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) # 设为不可编辑状态 - - #---------------------------------------------------------------------- - def registerEvent(self): - """""" - self.signal.connect(self.updateAccount) - self.__eventEngine.register(EVENT_ACCOUNT, self.signal.emit) - - #---------------------------------------------------------------------- - def updateAccount(self, event): - """""" - data = event.dict_['data'] - accountid = data['AccountID'] - - # 如果之前已经收到过这个账户的数据, 则直接更新 - if accountid in self.dictAccount: - d = self.dictAccount[accountid] - - for label, cell in d.items(): - cell.setText(str(data[label])) - # 否则插入新的一行,并更新 - else: - self.insertRow(0) - d = {} - - for col, label in enumerate(self.dictLabels.keys()): - cell = QtGui.QTableWidgetItem(str(data[label])) - self.setItem(0, col, cell) - d[label] = cell - - self.dictAccount[accountid] = d - - -######################################################################## -class TradeMonitor(QtGui.QTableWidget): - """用于显示成交记录""" - signal = QtCore.pyqtSignal(type(Event())) - - dictLabels = OrderedDict() - dictLabels['InstrumentID'] = u'合约代码' - dictLabels['ExchangeInstID'] = u'交易所合约代码' - dictLabels['ExchangeID'] = u'交易所' - dictLabels['Direction'] = u'方向' - dictLabels['OffsetFlag'] = u'开平' - dictLabels['TradeID'] = u'成交编号' - dictLabels['TradeTime'] = u'成交时间' - dictLabels['Volume'] = u'数量' - dictLabels['Price'] = u'价格' - dictLabels['OrderRef'] = u'报单号' - dictLabels['OrderSysID'] = u'报单系统号' - - dictDirection = {} - dictDirection['0'] = u'买' - dictDirection['1'] = u'卖' - dictDirection['2'] = u'ETF申购' - dictDirection['3'] = u'ETF赎回' - dictDirection['4'] = u'ETF现金替代' - dictDirection['5'] = u'债券入库' - dictDirection['6'] = u'债券出库' - dictDirection['7'] = u'配股' - dictDirection['8'] = u'转托管' - dictDirection['9'] = u'信用账户配股' - dictDirection['A'] = u'担保品买入' - dictDirection['B'] = u'担保品卖出' - dictDirection['C'] = u'担保品转入' - dictDirection['D'] = u'担保品转出' - dictDirection['E'] = u'融资买入' - dictDirection['F'] = u'融资卖出' - dictDirection['G'] = u'卖券还款' - dictDirection['H'] = u'买券还券' - dictDirection['I'] = u'直接还款' - dictDirection['J'] = u'直接换券' - dictDirection['K'] = u'余券划转' - dictDirection['L'] = u'OF申购' - dictDirection['M'] = u'OF赎回' - dictDirection['N'] = u'SF拆分' - dictDirection['O'] = u'SF合并' - dictDirection['P'] = u'备兑' - dictDirection['Q'] = u'证券冻结/解冻' - dictDirection['R'] = u'行权' - - dictOffset = {} - dictOffset['0'] = u'开仓' - dictOffset['1'] = u'平仓' - dictOffset['2'] = u'强平' - dictOffset['3'] = u'平今' - dictOffset['4'] = u'平昨' - dictOffset['5'] = u'强减' - dictOffset['6'] = u'本地强平' - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, parent=None): - """Constructor""" - super(TradeMonitor, self).__init__(parent) - self.__eventEngine = eventEngine - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - self.setWindowTitle(u'成交') - - self.setColumnCount(len(self.dictLabels)) - self.setHorizontalHeaderLabels(self.dictLabels.values()) - - self.verticalHeader().setVisible(False) # 关闭左边的垂直表头 - self.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) # 设为不可编辑状态 - - #---------------------------------------------------------------------- - def registerEvent(self): - """""" - self.signal.connect(self.updateTrade) - self.__eventEngine.register(EVENT_TRADE, self.signal.emit) - - #---------------------------------------------------------------------- - def updateTrade(self, event): - """""" - data = event.dict_['data'] - - self.insertRow(0) - - for col, label in enumerate(self.dictLabels.keys()): - if label == 'Direction': - try: - value = self.dictDirection[data[label]] - except KeyError: - value = u'未知类型' - elif label == 'OffsetFlag': - try: - value = self.dictOffset[data[label]] - except KeyError: - value = u'未知类型' - else: - value = str(data[label]) - - cell = QtGui.QTableWidgetItem(value) - self.setItem(0, col, cell) - - -######################################################################## -class PositionMonitor(QtGui.QTableWidget): - """用于显示持仓""" - signal = QtCore.pyqtSignal(type(Event())) - - dictLabels = OrderedDict() - dictLabels['InstrumentID'] = u'合约代码' - dictLabels['PosiDirection'] = u'方向' - dictLabels['Position'] = u'持仓' - dictLabels['LongFrozen'] = u'多头冻结' - dictLabels['ShortFrozen'] = u'空头冻结' - dictLabels['PositionCost'] = u'持仓成本' - dictLabels['OpenCost'] = u'开仓成本' - - dictPosiDirection = {} - dictPosiDirection['1'] = u'净' - dictPosiDirection['2'] = u'多' - dictPosiDirection['3'] = u'空' - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, parent=None): - """Constructor""" - super(PositionMonitor, self).__init__(parent) - self.__eventEngine = eventEngine - - self.dictPosition = {} # 用来保存持仓对应的单元格 - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - self.setWindowTitle(u'持仓') - - self.setColumnCount(len(self.dictLabels)) - self.setHorizontalHeaderLabels(self.dictLabels.values()) - - self.verticalHeader().setVisible(False) # 关闭左边的垂直表头 - self.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) # 设为不可编辑状态 - - #---------------------------------------------------------------------- - def registerEvent(self): - """""" - self.signal.connect(self.updatePosition) - self.__eventEngine.register(EVENT_POSITION, self.signal.emit) - - #---------------------------------------------------------------------- - def updatePosition(self, event): - """""" - data = event.dict_['data'] - - # 过滤返回值为空的情况 - if data['InstrumentID']: - posid = data['InstrumentID'] + '.' + data['PosiDirection'] - - # 如果之前已经收到过这个账户的数据, 则直接更新 - if posid in self.dictPosition: - d = self.dictPosition[posid] - - for label, cell in d.items(): - if label == 'PosiDirection': - try: - value = self.dictPosiDirection[data[label]] - except KeyError: - value = u'未知类型' - else: - value = str(data[label]) - cell.setText(value) - # 否则插入新的一行,并更新 - else: - self.insertRow(0) - d = {} - - for col, label in enumerate(self.dictLabels.keys()): - if label == 'PosiDirection': - try: - value = self.dictPosiDirection[data[label]] - except KeyError: - value = u'未知类型' - else: - value = str(data[label]) - cell = QtGui.QTableWidgetItem(value) - self.setItem(0, col, cell) - d[label] = cell - - self.dictPosition[posid] = d - - -######################################################################## -class OrderMonitor(QtGui.QTableWidget): - """用于显示所有报单""" - signal = QtCore.pyqtSignal(type(Event())) - - dictLabels = OrderedDict() - dictLabels['OrderRef'] = u'报单号' - dictLabels['OrderSysID'] = u'系统编号' - dictLabels['InstrumentID'] = u'合约代码' - dictLabels['ExchangeInstID'] = u'交易所合约代码' - dictLabels['Direction'] = u'方向' - dictLabels['CombOffsetFlag'] = u'开平' - dictLabels['LimitPrice'] = u'价格' - dictLabels['VolumeTotalOriginal'] = u'委托数量' - dictLabels['VolumeTraded'] = u'成交数量' - dictLabels['InsertTime'] = u'委托时间' - dictLabels['CancelTime'] = u'撤销时间' - dictLabels['StatusMsg'] = u'状态信息' - - dictDirection = {} - dictDirection['0'] = u'买' - dictDirection['1'] = u'卖' - dictDirection['2'] = u'ETF申购' - dictDirection['3'] = u'ETF赎回' - dictDirection['4'] = u'ETF现金替代' - dictDirection['5'] = u'债券入库' - dictDirection['6'] = u'债券出库' - dictDirection['7'] = u'配股' - dictDirection['8'] = u'转托管' - dictDirection['9'] = u'信用账户配股' - dictDirection['A'] = u'担保品买入' - dictDirection['B'] = u'担保品卖出' - dictDirection['C'] = u'担保品转入' - dictDirection['D'] = u'担保品转出' - dictDirection['E'] = u'融资买入' - dictDirection['F'] = u'融资卖出' - dictDirection['G'] = u'卖券还款' - dictDirection['H'] = u'买券还券' - dictDirection['I'] = u'直接还款' - dictDirection['J'] = u'直接换券' - dictDirection['K'] = u'余券划转' - dictDirection['L'] = u'OF申购' - dictDirection['M'] = u'OF赎回' - dictDirection['N'] = u'SF拆分' - dictDirection['O'] = u'SF合并' - dictDirection['P'] = u'备兑' - dictDirection['Q'] = u'证券冻结/解冻' - dictDirection['R'] = u'行权' - - dictOffset = {} - dictOffset['0'] = u'开仓' - dictOffset['1'] = u'平仓' - dictOffset['2'] = u'强平' - dictOffset['3'] = u'平今' - dictOffset['4'] = u'平昨' - dictOffset['5'] = u'强减' - dictOffset['6'] = u'本地强平' - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, mainEngine, parent=None): - """Constructor""" - super(OrderMonitor, self).__init__(parent) - self.__eventEngine = eventEngine - self.__mainEngine = mainEngine - - self.dictOrder = {} # 用来保存报单号对应的单元格对象 - self.dictOrderData = {} # 用来保存报单数据 - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - self.setWindowTitle(u'报单') - - self.setColumnCount(len(self.dictLabels)) - self.setHorizontalHeaderLabels(self.dictLabels.values()) - - self.verticalHeader().setVisible(False) # 关闭左边的垂直表头 - self.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) # 设为不可编辑状态 - - #---------------------------------------------------------------------- - def registerEvent(self): - """""" - self.signal.connect(self.updateOrder) - self.__eventEngine.register(EVENT_ORDER, self.signal.emit) - - self.itemDoubleClicked.connect(self.cancelOrder) - - #---------------------------------------------------------------------- - def updateOrder(self, event): - """""" - data = event.dict_['data'] - orderref = data['OrderRef'] - - self.dictOrderData[orderref] = data - - # 如果之前已经收到过这个账户的数据, 则直接更新 - if orderref in self.dictOrder: - d = self.dictOrder[orderref] - - for label, cell in d.items(): - if label == 'Direction': - try: - value = self.dictDirection[data[label]] - except KeyError: - value = u'未知类型' - elif label == 'CombOffsetFlag': - try: - value = self.dictOffset[data[label]] - except KeyError: - value = u'未知类型' - elif label == 'StatusMsg': - value = data[label].decode('gbk') - else: - value = str(data[label]) - - cell.setText(value) - # 否则插入新的一行,并更新 - else: - self.insertRow(0) - d = {} - - for col, label in enumerate(self.dictLabels.keys()): - if label == 'Direction': - try: - value = self.dictDirection[data[label]] - except KeyError: - value = u'未知类型' - elif label == 'CombOffsetFlag': - try: - value = self.dictOffset[data[label]] - except KeyError: - value = u'未知类型' - elif label == 'StatusMsg': - value = data[label].decode('gbk') - else: - value = str(data[label]) - - cell = QtGui.QTableWidgetItem(value) - self.setItem(0, col, cell) - d[label] = cell - - cell.orderref = orderref # 动态绑定报单号到单元格上 - - self.dictOrder[orderref] = d - - #---------------------------------------------------------------------- - def cancelOrder(self, cell): - """双击撤单""" - orderref = cell.orderref - order = self.dictOrderData[orderref] - - # 撤单前检查报单是否已经撤销或者全部成交 - if not (order['OrderStatus'] == '0' or order['OrderStatus'] == '5'): - self.__mainEngine.cancelOrder(order['InstrumentID'], - order['ExchangeID'], - orderref, - order['FrontID'], - order['SessionID']) - - #---------------------------------------------------------------------- - def cancelAll(self): - """全撤""" - for order in self.dictOrderData.values(): - if not (order['OrderStatus'] == '0' or order['OrderStatus'] == '5'): - self.__mainEngine.cancelOrder(order['InstrumentID'], - order['ExchangeID'], - order['OrderRef'], - order['FrontID'], - order['SessionID']) - - -######################################################################## -class MarketDataMonitor(QtGui.QTableWidget): - """用于显示行情""" - signal = QtCore.pyqtSignal(type(Event())) - - dictLabels = OrderedDict() - dictLabels['Name'] = u'合约名称' - dictLabels['InstrumentID'] = u'合约代码' - dictLabels['ExchangeInstID'] = u'合约交易所代码' - - dictLabels['BidPrice1'] = u'买一价' - dictLabels['BidVolume1'] = u'买一量' - dictLabels['AskPrice1'] = u'卖一价' - dictLabels['AskVolume1'] = u'卖一量' - - dictLabels['LastPrice'] = u'最新价' - dictLabels['Volume'] = u'成交量' - - dictLabels['UpdateTime'] = u'更新时间' - - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, mainEngine, parent=None): - """Constructor""" - super(MarketDataMonitor, self).__init__(parent) - self.__eventEngine = eventEngine - self.__mainEngine = mainEngine - - self.dictData = {} - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - self.setWindowTitle(u'行情') - - self.setColumnCount(len(self.dictLabels)) - self.setHorizontalHeaderLabels(self.dictLabels.values()) - - self.verticalHeader().setVisible(False) # 关闭左边的垂直表头 - self.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) # 设为不可编辑状态 - - #---------------------------------------------------------------------- - def registerEvent(self): - """""" - self.signal.connect(self.updateData) - self.__eventEngine.register(EVENT_MARKETDATA, self.signal.emit) - - #---------------------------------------------------------------------- - def updateData(self, event): - """""" - data = event.dict_['data'] - instrumentid = data['InstrumentID'] - - # 如果之前已经收到过这个账户的数据, 则直接更新 - if instrumentid in self.dictData: - d = self.dictData[instrumentid] - - for label, cell in d.items(): - if label != 'Name': - value = str(data[label]) - else: - value = self.getName(data['InstrumentID']) - cell.setText(value) - # 否则插入新的一行,并更新 - else: - row = self.rowCount() - self.insertRow(row) - d = {} - - for col, label in enumerate(self.dictLabels.keys()): - if label != 'Name': - value = str(data[label]) - cell = QtGui.QTableWidgetItem(value) - self.setItem(row, col, cell) - d[label] = cell - else: - name = self.getName(data['InstrumentID']) - cell = QtGui.QTableWidgetItem(name) - self.setItem(row, col, cell) - d[label] = cell - - self.dictData[instrumentid] = d - - #---------------------------------------------------------------------- - def getName(self, instrumentid): - """获取名称""" - instrument = self.__mainEngine.selectInstrument(instrumentid) - if instrument: - return instrument['InstrumentName'].decode('GBK') - else: - return '' - - -######################################################################## -class LoginWidget(QtGui.QDialog): - """登录""" - - #---------------------------------------------------------------------- - def __init__(self, mainEngine, parent=None): - """Constructor""" - super(LoginWidget, self).__init__() - self.__mainEngine = mainEngine - - self.initUi() - self.loadData() - - #---------------------------------------------------------------------- - def initUi(self): - """初始化界面""" - self.setWindowTitle(u'登录') - - # 设置组件 - labelUserID = QtGui.QLabel(u'账号:') - labelPassword = QtGui.QLabel(u'密码:') - labelMdAddress = QtGui.QLabel(u'行情服务器:') - labelTdAddress = QtGui.QLabel(u'交易服务器:') - labelBrokerID = QtGui.QLabel(u'经纪商代码') - - self.editUserID = QtGui.QLineEdit() - self.editPassword = QtGui.QLineEdit() - self.editMdAddress = QtGui.QLineEdit() - self.editTdAddress = QtGui.QLineEdit() - self.editBrokerID = QtGui.QLineEdit() - - self.editUserID.setMinimumWidth(200) - - buttonLogin = QtGui.QPushButton(u'登录') - buttonCancel = QtGui.QPushButton(u'取消') - buttonLogin.clicked.connect(self.login) - buttonCancel.clicked.connect(self.close) - - # 设置布局 - buttonHBox = QtGui.QHBoxLayout() - buttonHBox.addStretch() - buttonHBox.addWidget(buttonLogin) - buttonHBox.addWidget(buttonCancel) - - grid = QtGui.QGridLayout() - grid.addWidget(labelUserID, 0, 0) - grid.addWidget(labelPassword, 1, 0) - grid.addWidget(labelMdAddress, 2, 0) - grid.addWidget(labelTdAddress, 3, 0) - grid.addWidget(labelBrokerID, 4, 0) - grid.addWidget(self.editUserID, 0, 1) - grid.addWidget(self.editPassword, 1, 1) - grid.addWidget(self.editMdAddress, 2, 1) - grid.addWidget(self.editTdAddress, 3, 1) - grid.addWidget(self.editBrokerID, 4, 1) - grid.addLayout(buttonHBox, 5, 0, 1, 2) - - self.setLayout(grid) - - #---------------------------------------------------------------------- - def login(self): - """登录""" - userid = str(self.editUserID.text()) - password = str(self.editPassword.text()) - mdAddress = str(self.editMdAddress.text()) - tdAddress = str(self.editTdAddress.text()) - brokerid = str(self.editBrokerID.text()) - - self.__mainEngine.login(userid, password, brokerid, mdAddress, tdAddress) - self.close() - - #---------------------------------------------------------------------- - def loadData(self): - """读取数据""" - f = shelve.open('setting.vn') - - try: - setting = f['login'] - userid = setting['userid'] - password = setting['password'] - mdAddress = setting['mdAddress'] - tdAddress = setting['tdAddress'] - brokerid = setting['brokerid'] - - self.editUserID.setText(userid) - self.editPassword.setText(password) - self.editMdAddress.setText(mdAddress) - self.editTdAddress.setText(tdAddress) - self.editBrokerID.setText(brokerid) - except KeyError: - pass - - f.close() - - #---------------------------------------------------------------------- - def saveData(self): - """保存数据""" - setting = {} - setting['userid'] = str(self.editUserID.text()) - setting['password'] = str(self.editPassword.text()) - setting['mdAddress'] = str(self.editMdAddress.text()) - setting['tdAddress'] = str(self.editTdAddress.text()) - setting['brokerid'] = str(self.editBrokerID.text()) - - f = shelve.open('setting.vn') - f['login'] = setting - f.close() - - #---------------------------------------------------------------------- - def closeEvent(self, event): - """关闭事件处理""" - # 当窗口被关闭时,先保存登录数据,再关闭 - self.saveData() - event.accept() - - -######################################################################## -class ControlWidget(QtGui.QWidget): - """调用查询函数""" - - #---------------------------------------------------------------------- - def __init__(self, mainEngine, parent=None): - """Constructor""" - super(ControlWidget, self).__init__() - self.__mainEngine = mainEngine - - self.initUi() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - self.setWindowTitle(u'测试') - - buttonAccount = QtGui.QPushButton(u'查询账户') - buttonInvestor = QtGui.QPushButton(u'查询投资者') - buttonPosition = QtGui.QPushButton(u'查询持仓') - - buttonAccount.clicked.connect(self.__mainEngine.getAccount) - buttonInvestor.clicked.connect(self.__mainEngine.getInvestor) - buttonPosition.clicked.connect(self.__mainEngine.getPosition) - - hBox = QtGui.QHBoxLayout() - hBox.addWidget(buttonAccount) - hBox.addWidget(buttonInvestor) - hBox.addWidget(buttonPosition) - - self.setLayout(hBox) - - -######################################################################## -class TradingWidget(QtGui.QWidget): - """交易""" - signal = QtCore.pyqtSignal(type(Event())) - - dictDirection = OrderedDict() - dictDirection['0'] = u'买' - dictDirection['1'] = u'卖' - - dictOffset = OrderedDict() - dictOffset['0'] = u'开仓' - dictOffset['1'] = u'平仓' - dictOffset['3'] = u'平今' - - dictPriceType = OrderedDict() - dictPriceType['1'] = u'任意价' - dictPriceType['2'] = u'限价' - dictPriceType['3'] = u'最优价' - dictPriceType['4'] = u'最新价' - - # 反转字典 - dictDirectionReverse = {value:key for key,value in dictDirection.items()} - dictOffsetReverse = {value:key for key, value in dictOffset.items()} - dictPriceTypeReverse = {value:key for key, value in dictPriceType.items()} - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, mainEngine, orderMonitor, parent=None): - """Constructor""" - super(TradingWidget, self).__init__() - self.__eventEngine = eventEngine - self.__mainEngine = mainEngine - self.__orderMonitor = orderMonitor - - self.instrumentid = '' - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """初始化界面""" - self.setWindowTitle(u'交易') - - # 左边部分 - labelID = QtGui.QLabel(u'代码') - labelName = QtGui.QLabel(u'名称') - labelDirection = QtGui.QLabel(u'委托类型') - labelOffset = QtGui.QLabel(u'开平') - labelPrice = QtGui.QLabel(u'价格') - labelVolume = QtGui.QLabel(u'数量') - labelPriceType = QtGui.QLabel(u'价格类型') - - self.lineID = QtGui.QLineEdit() - self.lineName = QtGui.QLineEdit() - - self.comboDirection = QtGui.QComboBox() - self.comboDirection.addItems(self.dictDirection.values()) - - self.comboOffset = QtGui.QComboBox() - self.comboOffset.addItems(self.dictOffset.values()) - - self.spinPrice = QtGui.QDoubleSpinBox() - self.spinPrice.setDecimals(4) - self.spinPrice.setMinimum(0) - self.spinPrice.setMaximum(10000) - - self.spinVolume = QtGui.QSpinBox() - self.spinVolume.setMinimum(0) - self.spinVolume.setMaximum(1000000) - - self.comboPriceType = QtGui.QComboBox() - self.comboPriceType.addItems(self.dictPriceType.values()) - - gridleft = QtGui.QGridLayout() - gridleft.addWidget(labelID, 0, 0) - gridleft.addWidget(labelName, 1, 0) - gridleft.addWidget(labelDirection, 2, 0) - gridleft.addWidget(labelOffset, 3, 0) - gridleft.addWidget(labelPrice, 4, 0) - gridleft.addWidget(labelVolume, 5, 0) - gridleft.addWidget(labelPriceType, 6, 0) - gridleft.addWidget(self.lineID, 0, 1) - gridleft.addWidget(self.lineName, 1, 1) - gridleft.addWidget(self.comboDirection, 2, 1) - gridleft.addWidget(self.comboOffset, 3, 1) - gridleft.addWidget(self.spinPrice, 4, 1) - gridleft.addWidget(self.spinVolume, 5, 1) - gridleft.addWidget(self.comboPriceType, 6, 1) - - # 右边部分 - labelBid1 = QtGui.QLabel(u'买一') - labelBid2 = QtGui.QLabel(u'买二') - labelBid3 = QtGui.QLabel(u'买三') - labelBid4 = QtGui.QLabel(u'买四') - labelBid5 = QtGui.QLabel(u'买五') - - labelAsk1 = QtGui.QLabel(u'卖一') - labelAsk2 = QtGui.QLabel(u'卖二') - labelAsk3 = QtGui.QLabel(u'卖三') - labelAsk4 = QtGui.QLabel(u'卖四') - labelAsk5 = QtGui.QLabel(u'卖五') - - self.labelBidPrice1 = QtGui.QLabel() - self.labelBidPrice2 = QtGui.QLabel() - self.labelBidPrice3 = QtGui.QLabel() - self.labelBidPrice4 = QtGui.QLabel() - self.labelBidPrice5 = QtGui.QLabel() - self.labelBidVolume1 = QtGui.QLabel() - self.labelBidVolume2 = QtGui.QLabel() - self.labelBidVolume3 = QtGui.QLabel() - self.labelBidVolume4 = QtGui.QLabel() - self.labelBidVolume5 = QtGui.QLabel() - - self.labelAskPrice1 = QtGui.QLabel() - self.labelAskPrice2 = QtGui.QLabel() - self.labelAskPrice3 = QtGui.QLabel() - self.labelAskPrice4 = QtGui.QLabel() - self.labelAskPrice5 = QtGui.QLabel() - self.labelAskVolume1 = QtGui.QLabel() - self.labelAskVolume2 = QtGui.QLabel() - self.labelAskVolume3 = QtGui.QLabel() - self.labelAskVolume4 = QtGui.QLabel() - self.labelAskVolume5 = QtGui.QLabel() - - labelLast = QtGui.QLabel(u'最新') - self.labelLastPrice = QtGui.QLabel() - self.labelReturn = QtGui.QLabel() - - self.labelLastPrice.setMinimumWidth(60) - self.labelReturn.setMinimumWidth(60) - - gridRight = QtGui.QGridLayout() - gridRight.addWidget(labelAsk5, 0, 0) - gridRight.addWidget(labelAsk4, 1, 0) - gridRight.addWidget(labelAsk3, 2, 0) - gridRight.addWidget(labelAsk2, 3, 0) - gridRight.addWidget(labelAsk1, 4, 0) - gridRight.addWidget(labelLast, 5, 0) - gridRight.addWidget(labelBid1, 6, 0) - gridRight.addWidget(labelBid2, 7, 0) - gridRight.addWidget(labelBid3, 8, 0) - gridRight.addWidget(labelBid4, 9, 0) - gridRight.addWidget(labelBid5, 10, 0) - - gridRight.addWidget(self.labelAskPrice5, 0, 1) - gridRight.addWidget(self.labelAskPrice4, 1, 1) - gridRight.addWidget(self.labelAskPrice3, 2, 1) - gridRight.addWidget(self.labelAskPrice2, 3, 1) - gridRight.addWidget(self.labelAskPrice1, 4, 1) - gridRight.addWidget(self.labelLastPrice, 5, 1) - gridRight.addWidget(self.labelBidPrice1, 6, 1) - gridRight.addWidget(self.labelBidPrice2, 7, 1) - gridRight.addWidget(self.labelBidPrice3, 8, 1) - gridRight.addWidget(self.labelBidPrice4, 9, 1) - gridRight.addWidget(self.labelBidPrice5, 10, 1) - - gridRight.addWidget(self.labelAskVolume5, 0, 2) - gridRight.addWidget(self.labelAskVolume4, 1, 2) - gridRight.addWidget(self.labelAskVolume3, 2, 2) - gridRight.addWidget(self.labelAskVolume2, 3, 2) - gridRight.addWidget(self.labelAskVolume1, 4, 2) - gridRight.addWidget(self.labelReturn, 5, 2) - gridRight.addWidget(self.labelBidVolume1, 6, 2) - gridRight.addWidget(self.labelBidVolume2, 7, 2) - gridRight.addWidget(self.labelBidVolume3, 8, 2) - gridRight.addWidget(self.labelBidVolume4, 9, 2) - gridRight.addWidget(self.labelBidVolume5, 10, 2) - - # 发单按钮 - buttonSendOrder = QtGui.QPushButton(u'发单') - buttonCancelAll = QtGui.QPushButton(u'全撤') - - # 整合布局 - hbox = QtGui.QHBoxLayout() - hbox.addLayout(gridleft) - hbox.addLayout(gridRight) - - vbox = QtGui.QVBoxLayout() - vbox.addLayout(hbox) - vbox.addWidget(buttonSendOrder) - vbox.addWidget(buttonCancelAll) - - self.setLayout(vbox) - - # 关联更新 - buttonSendOrder.clicked.connect(self.sendOrder) - buttonCancelAll.clicked.connect(self.__orderMonitor.cancelAll) - self.lineID.returnPressed.connect(self.updateID) - - #---------------------------------------------------------------------- - def updateID(self): - """合约变化""" - instrumentid = str(self.lineID.text()) - - # 获取合约 - instrument = self.__mainEngine.selectInstrument(instrumentid) - if instrument: - self.lineName.setText(instrument['InstrumentName'].decode('GBK')) - - # 清空价格数量 - self.spinPrice.setValue(0) - self.spinVolume.setValue(0) - - # 清空行情显示 - self.labelBidPrice1.setText('') - self.labelBidPrice2.setText('') - self.labelBidPrice3.setText('') - self.labelBidPrice4.setText('') - self.labelBidPrice5.setText('') - self.labelBidVolume1.setText('') - self.labelBidVolume2.setText('') - self.labelBidVolume3.setText('') - self.labelBidVolume4.setText('') - self.labelBidVolume5.setText('') - self.labelAskPrice1.setText('') - self.labelAskPrice2.setText('') - self.labelAskPrice3.setText('') - self.labelAskPrice4.setText('') - self.labelAskPrice5.setText('') - self.labelAskVolume1.setText('') - self.labelAskVolume2.setText('') - self.labelAskVolume3.setText('') - self.labelAskVolume4.setText('') - self.labelAskVolume5.setText('') - self.labelLastPrice.setText('') - self.labelReturn.setText('') - - # 重新注册事件监听 - self.__eventEngine.unregister(EVENT_MARKETDATA_CONTRACT+self.instrumentid, self.signal.emit) - self.__eventEngine.register(EVENT_MARKETDATA_CONTRACT+instrumentid, self.signal.emit) - - # 订阅合约 - self.__mainEngine.subscribe(instrumentid, instrument['ExchangeID']) - - # 更新目前的合约 - self.instrumentid = instrumentid - - #---------------------------------------------------------------------- - def updateMarketData(self, event): - """更新行情""" - data = event.dict_['data'] - - if data['InstrumentID'] == self.instrumentid: - self.labelBidPrice1.setText(str(data['BidPrice1'])) - self.labelAskPrice1.setText(str(data['AskPrice1'])) - self.labelBidVolume1.setText(str(data['BidVolume1'])) - self.labelAskVolume1.setText(str(data['AskVolume1'])) - - if data['BidVolume2']: - self.labelBidPrice2.setText(str(data['BidPrice2'])) - self.labelBidPrice3.setText(str(data['BidPrice3'])) - self.labelBidPrice4.setText(str(data['BidPrice4'])) - self.labelBidPrice5.setText(str(data['BidPrice5'])) - - self.labelAskPrice2.setText(str(data['AskPrice2'])) - self.labelAskPrice3.setText(str(data['AskPrice3'])) - self.labelAskPrice4.setText(str(data['AskPrice4'])) - self.labelAskPrice5.setText(str(data['AskPrice5'])) - - self.labelBidVolume2.setText(str(data['BidVolume2'])) - self.labelBidVolume3.setText(str(data['BidVolume3'])) - self.labelBidVolume4.setText(str(data['BidVolume4'])) - self.labelBidVolume5.setText(str(data['BidVolume5'])) - - self.labelAskVolume2.setText(str(data['AskVolume2'])) - self.labelAskVolume3.setText(str(data['AskVolume3'])) - self.labelAskVolume4.setText(str(data['AskVolume4'])) - self.labelAskVolume5.setText(str(data['AskVolume5'])) - - self.labelLastPrice.setText(str(data['LastPrice'])) - rt = (data['LastPrice']/data['PreClosePrice'])-1 - self.labelReturn.setText(('%.2f' %(rt*100))+'%') - - #---------------------------------------------------------------------- - def registerEvent(self): - """注册事件监听""" - self.signal.connect(self.updateMarketData) - - #---------------------------------------------------------------------- - def sendOrder(self): - """发单""" - instrumentid = str(self.lineID.text()) - - instrument = self.__mainEngine.selectInstrument(instrumentid) - if instrument: - exchangeid = instrument['ExchangeID'] - direction = self.dictDirectionReverse[unicode(self.comboDirection.currentText())] - offset = self.dictOffsetReverse[unicode(self.comboOffset.currentText())] - price = float(self.spinPrice.value()) - volume = int(self.spinVolume.value()) - pricetype = self.dictPriceTypeReverse[unicode(self.comboPriceType.currentText())] - self.__mainEngine.sendOrder(instrumentid, exchangeid, price, pricetype, volume ,direction, offset) - - -######################################################################## -class AboutWidget(QtGui.QDialog): - """显示关于信息""" - - #---------------------------------------------------------------------- - def __init__(self, parent): - """Constructor""" - super(AboutWidget, self).__init__(parent) - - self.initUi() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - self.setWindowTitle(u'关于') - - text = u""" - vn.py框架Demo - - 完成日期:2015/4/17 - - 作者:用Python的交易员 - - License:MIT - - 主页:vnpy.org - - Github:github.com/vnpy/vnpy - - QQ交流群:262656087 - - - - - 开发环境 - - 操作系统:Windows 7 专业版 64位 - - Python发行版:Python 2.7.6 (Anaconda 1.9.2 Win-32) - - 图形库:PyQt4 4.11.3 Py2.7-x32 - - 交易接口:vn.lts/vn.ctp - - 事件驱动引擎:vn.event - - 开发环境:WingIDE 5.0.6 - - EXE打包:Nuitka 0.5.12.1 Python2.7 32 bit MSI - """ - - label = QtGui.QLabel() - label.setText(text) - label.setMinimumWidth(450) - - vbox = QtGui.QVBoxLayout() - vbox.addWidget(label) - - self.setLayout(vbox) - - -######################################################################## -class MainWindow(QtGui.QMainWindow): - """主窗口""" - signalInvestor = QtCore.pyqtSignal(type(Event())) - signalLog = QtCore.pyqtSignal(type(Event())) - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, mainEngine): - """Constructor""" - super(MainWindow, self).__init__() - self.__eventEngine = eventEngine - self.__mainEngine = mainEngine - - self.initUi() - self.registerEvent() - - #---------------------------------------------------------------------- - def initUi(self): - """""" - # 设置名称 - self.setWindowTitle(u'欢迎使用vn.py框架Demo') - - # 布局设置 - self.logM = LogMonitor(self.__eventEngine, self) - self.accountM = AccountMonitor(self.__eventEngine, self) - self.positionM = PositionMonitor(self.__eventEngine, self) - self.tradeM = TradeMonitor(self.__eventEngine, self) - self.orderM = OrderMonitor(self.__eventEngine, self.__mainEngine, self) - self.marketdataM = MarketDataMonitor(self.__eventEngine, self.__mainEngine, self) - self.tradingW = TradingWidget(self.__eventEngine, self.__mainEngine, self.orderM, self) - - righttab = QtGui.QTabWidget() - righttab.addTab(self.positionM, u'持仓') - righttab.addTab(self.accountM, u'账户') - - lefttab = QtGui.QTabWidget() - lefttab.addTab(self.orderM, u'报单') - lefttab.addTab(self.tradeM, u'成交') - lefttab.addTab(self.logM, u'日志') - - self.tradingW.setMaximumWidth(400) - tradingVBox = QtGui.QVBoxLayout() - tradingVBox.addWidget(self.tradingW) - tradingVBox.addStretch() - - upHBox = QtGui.QHBoxLayout() - upHBox.addLayout(tradingVBox) - upHBox.addWidget(self.marketdataM) - - downHBox = QtGui.QHBoxLayout() - downHBox.addWidget(lefttab) - downHBox.addWidget(righttab) - - vBox = QtGui.QVBoxLayout() - vBox.addLayout(upHBox) - vBox.addLayout(downHBox) - - centralwidget = QtGui.QWidget() - centralwidget.setLayout(vBox) - self.setCentralWidget(centralwidget) - - # 设置状态栏 - self.bar = self.statusBar() - self.bar.showMessage(u'启动Demo') - - # 设置菜单栏 - actionLogin = QtGui.QAction(u'登录', self) - actionLogin.triggered.connect(self.openLoginWidget) - actionExit = QtGui.QAction(u'退出', self) - actionExit.triggered.connect(self.close) - - actionAbout = QtGui.QAction(u'关于', self) - actionAbout.triggered.connect(self.openAboutWidget) - - menubar = self.menuBar() - sysMenu = menubar.addMenu(u'系统') - sysMenu.addAction(actionLogin) - sysMenu.addAction(actionExit) - - helpMenu = menubar.addMenu(u'帮助') - helpMenu.addAction(actionAbout) - - #---------------------------------------------------------------------- - def registerEvent(self): - """""" - self.signalInvestor.connect(self.updateInvestor) - self.signalLog.connect(self.updateLog) - - self.__eventEngine.register(EVENT_INVESTOR, self.signalInvestor.emit) - self.__eventEngine.register(EVENT_LOG, self.signalLog.emit) - - #---------------------------------------------------------------------- - def updateInvestor(self, event): - """""" - data = event.dict_['data'] - - self.setWindowTitle(u'欢迎使用vn.py框架Demo ' + data['InvestorName'].decode('GBK')) - - #---------------------------------------------------------------------- - def updateLog(self, event): - """""" - log = event.dict_['log'] - - self.bar.showMessage(log) - - #---------------------------------------------------------------------- - def openLoginWidget(self): - """打开登录""" - try: - self.loginW.show() - except AttributeError: - self.loginW = LoginWidget(self.__mainEngine, self) - self.loginW.show() - - #---------------------------------------------------------------------- - def openAboutWidget(self): - """打开关于""" - try: - self.aboutW.show() - except AttributeError: - self.aboutW = AboutWidget(self) - self.aboutW.show() - - #---------------------------------------------------------------------- - def closeEvent(self, event): - """退出事件处理""" - reply = QtGui.QMessageBox.question(self, u'退出', - u'确认退出?', QtGui.QMessageBox.Yes | - QtGui.QMessageBox.No, QtGui.QMessageBox.No) - - if reply == QtGui.QMessageBox.Yes: - self.__mainEngine.exit() - event.accept() - else: - event.ignore() - - - - - - - - - - diff --git a/vn.strategy/strategydemo/strategyEngine.py b/vn.strategy/strategydemo/strategyEngine.py index 221b0463..78c778c2 100644 --- a/vn.strategy/strategydemo/strategyEngine.py +++ b/vn.strategy/strategydemo/strategyEngine.py @@ -174,7 +174,6 @@ class StrategyEngine(object): # 调用函数 self.__connectMongo() - self.createStrategy() self.__registerEvent() #---------------------------------------------------------------------- @@ -182,7 +181,13 @@ class StrategyEngine(object): """创建策略""" strategy = strategyClass(strategyName, strategySymbol, self) self.dictStrategy[strategyName] = strategy - strategy.initSetting(strategySetting) + strategy.loadSetting(strategySetting) + + # 订阅合约行情,注意这里因为是CTP,所以ExchangeID可以忽略 + self.mainEngine.subscribe(strategySymbol, None) + + # 注册策略监听 + self.registerStrategy(strategySymbol, strategy) #---------------------------------------------------------------------- def __connectMongo(self): @@ -333,7 +338,7 @@ class StrategyEngine(object): order.orderRef = data['OrderRef'] order.direction = data['Direction'] - order.offset = data['ComboOffsetFlag'] + order.offset = data['CombOffsetFlag'] order.price = data['LimitPrice'] order.volumeOriginal = data['VolumeTotalOriginal'] @@ -355,8 +360,10 @@ class StrategyEngine(object): #---------------------------------------------------------------------- def __updateTrade(self, event): """成交更新""" + print 'updateTrade' data = event.dict_['data'] orderRef = data['OrderRef'] + print 'trade:', orderRef if orderRef in self.__dictOrderRefStrategy: @@ -398,6 +405,8 @@ class StrategyEngine(object): offset) self.__dictOrderRefStrategy[ref] = strategy + print 'ref:', ref + print 'strategy:', strategy.name return ref @@ -504,7 +513,7 @@ class StrategyTemplate(object): """Constructor""" self.name = name # 策略名称(注意唯一性) self.symbol = symbol # 策略交易的合约 - self.__engine = engine # 策略引擎对象 + self.engine = engine # 策略引擎对象 self.trading = False # 策略是否启动交易 @@ -541,6 +550,7 @@ class StrategyTemplate(object): 有需要可以重新实现更复杂的操作 """ self.trading = True + self.engine.writeLog(self.name + u'开始运行') #---------------------------------------------------------------------- def stop(self): @@ -549,83 +559,84 @@ class StrategyTemplate(object): 同上 """ self.trading = False + self.engine.writeLog(self.name + u'停止运行') #---------------------------------------------------------------------- - def initSetting(self, setting): + def loadSetting(self, setting): """ - 初始化设置 + 载入设置 setting通常是一个包含了参数设置的字典 """ raise NotImplementedError #---------------------------------------------------------------------- - def __buy(self, price, volume, stopOrder=False): + def buy(self, price, volume, stopOrder=False): """买入开仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_BUY, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY, OFFSET_OPEN, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_BUY, + ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY, OFFSET_OPEN, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __cover(self, price, volume, StopOrder=False): + def cover(self, price, volume, StopOrder=False): """买入平仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_BUY, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY, OFFSET_CLOSE, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_BUY, + ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY, OFFSET_CLOSE, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __sell(self, price, volume, stopOrder=False): + def sell(self, price, volume, stopOrder=False): """卖出平仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_SELL, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL, OFFSET_CLOSE, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_SELL, + ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL, OFFSET_CLOSE, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __short(self, price, volume, stopOrder=False): + def short(self, price, volume, stopOrder=False): """卖出开仓""" if self.trading: if stopOrder: - so = self.__engine.placeStopOrder(self.symbol, DIRECTION_SELL, + so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL, OFFSET_OPEN, price, volume, self) return so else: - ref = self.__engine.sendOrder(self.symbol, DIRECTION_SELL, + ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL, OFFSET_OPEN, price, volume, self) return ref else: return None #---------------------------------------------------------------------- - def __cancelOrder(self, orderRef): + def cancelOrder(self, orderRef): """撤单""" - self.__engine.cancelOrder(orderRef) + self.engine.cancelOrder(orderRef) #---------------------------------------------------------------------- - def __cancelStopOrder(self, so): + def cancelStopOrder(self, so): """撤销停止单""" - self.__engine.cancelStopOrder(so) + self.engine.cancelStopOrder(so) \ No newline at end of file