# 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['FrozenTransferFee'] = u'冻结过户费' dictLabels['FrozenStampTax'] = u'冻结印花税' dictLabels['Commission'] = u'手续费' dictLabels['TransferFee'] = u'过户费' dictLabels['StampTax'] = u'印花税' dictLabels['CurrMargin'] = u'当前保证金' dictLabels['Available'] = u'可用资金' dictLabels['WithdrawQuota'] = 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'开仓成本' dictLabels['StockValue'] = 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): """双击撤单""" print u'撤单指令' 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']) print u'发出撤单' #---------------------------------------------------------------------- def cancelAll(self): """全撤""" print u'全撤' 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'账号:') labelMdPassword = QtGui.QLabel(u'行情密码:') labelTdPassword = QtGui.QLabel(u'交易密码:') labelMdAddress = QtGui.QLabel(u'行情服务器:') labelTdAddress = QtGui.QLabel(u'交易服务器:') self.editUserID = QtGui.QLineEdit() self.editMdPassword = QtGui.QLineEdit() self.editTdPassword = QtGui.QLineEdit() self.editMdAddress = QtGui.QLineEdit() self.editTdAddress = 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(labelMdPassword, 1, 0) grid.addWidget(labelTdPassword, 2, 0) grid.addWidget(labelMdAddress, 3, 0) grid.addWidget(labelTdAddress, 4, 0) grid.addWidget(self.editUserID, 0, 1) grid.addWidget(self.editMdPassword, 1, 1) grid.addWidget(self.editTdPassword, 2, 1) grid.addWidget(self.editMdAddress, 3, 1) grid.addWidget(self.editTdAddress, 4, 1) grid.addLayout(buttonHBox, 5, 0, 1, 2) self.setLayout(grid) #---------------------------------------------------------------------- def login(self): """登录""" userid = str(self.editUserID.text()) mdPassword = str(self.editMdPassword.text()) tdPassword = str(self.editTdPassword.text()) mdAddress = str(self.editMdAddress.text()) tdAddress = str(self.editTdAddress.text()) brokerid = '2011' self.__mainEngine.login(userid, mdPassword, tdPassword, brokerid, mdAddress, tdAddress) self.close() #---------------------------------------------------------------------- def loadData(self): """读取数据""" f = shelve.open('setting.vn') try: setting = f['login'] userid = setting['userid'] mdPassword = setting['mdPassword'] tdPassword = setting['tdPassword'] mdAddress = setting['mdAddress'] tdAddress = setting['tdAddress'] self.editUserID.setText(userid) self.editMdPassword.setText(mdPassword) self.editTdPassword.setText(tdPassword) self.editMdAddress.setText(mdAddress) self.editTdAddress.setText(tdAddress) except KeyError: pass f.close() #---------------------------------------------------------------------- def saveData(self): """保存数据""" setting = {} setting['userid'] = str(self.editUserID.text()) setting['mdPassword'] = str(self.editMdPassword.text()) setting['tdPassword'] = str(self.editTdPassword.text()) setting['mdAddress'] = str(self.editMdAddress.text()) setting['tdAddress'] = str(self.editTdAddress.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'卖' dictDirection['2'] = u'ETF申购' dictDirection['3'] = u'ETF赎回' 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 = OrderedDict() dictOffset['0'] = u'开仓' dictOffset['1'] = u'平仓' dictPriceType = OrderedDict() dictPriceType['1'] = u'市价' dictPriceType['2'] = u'限价' dictPriceType['3'] = u'最优五档剩余撤销' dictPriceType['4'] = u'最优五档剩余限价' dictPriceType['5'] = u'全部成交或撤销' dictPriceType['6'] = u'本方最优市价' dictPriceType['7'] = 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.labelBidPrice2.setText(str(data['BidPrice2'])) self.labelBidPrice3.setText(str(data['BidPrice3'])) self.labelBidPrice4.setText(str(data['BidPrice4'])) self.labelBidPrice5.setText(str(data['BidPrice5'])) self.labelAskPrice1.setText(str(data['AskPrice1'])) 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.labelBidVolume1.setText(str(data['BidVolume1'])) 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.labelAskVolume1.setText(str(data['AskVolume1'])) 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()