# encoding: UTF-8 import json import csv import os from collections import OrderedDict from PyQt4 import QtGui, QtCore from eventEngine import * from vtFunction import * from vtGateway import * #---------------------------------------------------------------------- def loadFont(): """载入字体设置""" fileName = 'VT_setting.json' path = os.path.abspath(os.path.dirname(__file__)) fileName = os.path.join(path, fileName) try: f = file(fileName) setting = json.load(f) family = setting['fontFamily'] size = setting['fontSize'] font = QtGui.QFont(family, size) except: font = QtGui.QFont(u'微软雅黑', 12) return font BASIC_FONT = loadFont() ######################################################################## class BasicCell(QtGui.QTableWidgetItem): """基础的单元格""" #---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(BasicCell, self).__init__() self.data = None if text: self.setContent(text) #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" if text == '0' or text == '0.0': self.setText('') else: self.setText(text) ######################################################################## class NumCell(QtGui.QTableWidgetItem): """用来显示数字的单元格""" #---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(NumCell, self).__init__() self.data = None if text: self.setContent(text) #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" # 考虑到NumCell主要用来显示OrderID和TradeID之类的整数字段, # 这里的数据转化方式使用int类型。但是由于部分交易接口的委托 # 号和成交号可能不是纯数字的形式,因此补充了一个try...except try: num = int(text) self.setData(QtCore.Qt.DisplayRole, num) except ValueError: self.setText(text) ######################################################################## class DirectionCell(QtGui.QTableWidgetItem): """用来显示买卖方向的单元格""" #---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(DirectionCell, self).__init__() self.data = None if text: self.setContent(text) #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" if text == DIRECTION_LONG or text == DIRECTION_NET: self.setForeground(QtGui.QColor('red')) elif text == DIRECTION_SHORT: self.setForeground(QtGui.QColor('green')) self.setText(text) ######################################################################## class NameCell(QtGui.QTableWidgetItem): """用来显示合约中文的单元格""" #---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(NameCell, self).__init__() self.mainEngine = mainEngine self.data = None if text: self.setContent(text) #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" if self.mainEngine: # 首先尝试正常获取合约对象 contract = self.mainEngine.getContract(text) # 如果能读取合约信息 if contract: self.setText(contract.name) ######################################################################## class BidCell(QtGui.QTableWidgetItem): """买价单元格""" #---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(BidCell, self).__init__() self.data = None self.setForeground(QtGui.QColor('black')) self.setBackground(QtGui.QColor(255,174,201)) if text: self.setContent(text) #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" self.setText(text) ######################################################################## class AskCell(QtGui.QTableWidgetItem): """买价单元格""" #---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(AskCell, self).__init__() self.data = None self.setForeground(QtGui.QColor('black')) self.setBackground(QtGui.QColor(160,255,160)) if text: self.setContent(text) #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" self.setText(text) ######################################################################## class BasicMonitor(QtGui.QTableWidget): """ 基础监控 headerDict中的值对应的字典格式如下 {'chinese': u'中文名', 'cellType': BasicCell} """ signal = QtCore.pyqtSignal(type(Event())) #---------------------------------------------------------------------- def __init__(self, mainEngine=None, eventEngine=None, parent=None): """Constructor""" super(BasicMonitor, self).__init__(parent) self.mainEngine = mainEngine self.eventEngine = eventEngine # 保存表头标签用 self.headerDict = OrderedDict() # 有序字典,key是英文名,value是对应的配置字典 self.headerList = [] # 对应self.headerDict.keys() # 保存相关数据用 self.dataDict = {} # 字典,key是字段对应的数据,value是保存相关单元格的字典 self.dataKey = '' # 字典键对应的数据字段 # 监控的事件类型 self.eventType = '' # 字体 self.font = None # 保存数据对象到单元格 self.saveData = False # 默认不允许根据表头进行排序,需要的组件可以开启 self.sorting = False # 初始化右键菜单 self.initMenu() #---------------------------------------------------------------------- def setHeaderDict(self, headerDict): """设置表头有序字典""" self.headerDict = headerDict self.headerList = headerDict.keys() #---------------------------------------------------------------------- def setDataKey(self, dataKey): """设置数据字典的键""" self.dataKey = dataKey #---------------------------------------------------------------------- def setEventType(self, eventType): """设置监控的事件类型""" self.eventType = eventType #---------------------------------------------------------------------- def setFont(self, font): """设置字体""" self.font = font #---------------------------------------------------------------------- def setSaveData(self, saveData): """设置是否要保存数据到单元格""" self.saveData = saveData #---------------------------------------------------------------------- def initTable(self): """初始化表格""" # 设置表格的列数 col = len(self.headerDict) self.setColumnCount(col) # 设置列表头 labels = [d['chinese'] for d in self.headerDict.values()] self.setHorizontalHeaderLabels(labels) # 关闭左边的垂直表头 self.verticalHeader().setVisible(False) # 设为不可编辑 self.setEditTriggers(self.NoEditTriggers) # 设为行交替颜色 self.setAlternatingRowColors(True) # 设置允许排序 self.setSortingEnabled(self.sorting) #---------------------------------------------------------------------- def registerEvent(self): """注册GUI更新相关的事件监听""" self.signal.connect(self.updateEvent) self.eventEngine.register(self.eventType, self.signal.emit) #---------------------------------------------------------------------- def updateEvent(self, event): """收到事件更新""" data = event.dict_['data'] self.updateData(data) #---------------------------------------------------------------------- def updateData(self, data): """将数据更新到表格中""" # 如果允许了排序功能,则插入数据前必须关闭,否则插入新的数据会变乱 if self.sorting: self.setSortingEnabled(False) # 如果设置了dataKey,则采用存量更新模式 if self.dataKey: key = data.__getattribute__(self.dataKey) # 如果键在数据字典中不存在,则先插入新的一行,并创建对应单元格 if key not in self.dataDict: self.insertRow(0) d = {} for n, header in enumerate(self.headerList): content = safeUnicode(data.__getattribute__(header)) cellType = self.headerDict[header]['cellType'] cell = cellType(content, self.mainEngine) if self.font: cell.setFont(self.font) # 如果设置了特殊字体,则进行单元格设置 if self.saveData: # 如果设置了保存数据对象,则进行对象保存 cell.data = data self.setItem(0, n, cell) d[header] = cell self.dataDict[key] = d # 否则如果已经存在,则直接更新相关单元格 else: d = self.dataDict[key] for header in self.headerList: content = safeUnicode(data.__getattribute__(header)) cell = d[header] cell.setContent(content) if self.saveData: # 如果设置了保存数据对象,则进行对象保存 cell.data = data # 否则采用增量更新模式 else: self.insertRow(0) for n, header in enumerate(self.headerList): content = safeUnicode(data.__getattribute__(header)) cellType = self.headerDict[header]['cellType'] cell = cellType(content, self.mainEngine) if self.font: cell.setFont(self.font) if self.saveData: cell.data = data self.setItem(0, n, cell) # 调整列宽 self.resizeColumns() # 重新打开排序 if self.sorting: self.setSortingEnabled(True) #---------------------------------------------------------------------- def resizeColumns(self): """调整各列的大小""" self.horizontalHeader().resizeSections(QtGui.QHeaderView.ResizeToContents) #---------------------------------------------------------------------- def setSorting(self, sorting): """设置是否允许根据表头排序""" self.sorting = sorting #---------------------------------------------------------------------- def saveToCsv(self): """保存表格内容到CSV文件""" # 先隐藏右键菜单 self.menu.close() # 获取想要保存的文件名 path = QtGui.QFileDialog.getSaveFileName(self, '保存数据', '', 'CSV(*.csv)') try: if not path.isEmpty(): with open(unicode(path), 'wb') as f: writer = csv.writer(f) # 保存标签 headers = [header.encode('gbk') for header in self.headerList] writer.writerow(headers) # 保存每行内容 for row in range(self.rowCount()): rowdata = [] for column in range(self.columnCount()): item = self.item(row, column) if item is not None: rowdata.append( unicode(item.text()).encode('gbk')) else: rowdata.append('') writer.writerow(rowdata) except IOError: pass #---------------------------------------------------------------------- def initMenu(self): """初始化右键菜单""" self.menu = QtGui.QMenu(self) saveAction = QtGui.QAction(u'保存内容', self) saveAction.triggered.connect(self.saveToCsv) self.menu.addAction(saveAction) #---------------------------------------------------------------------- def contextMenuEvent(self, event): """右键点击事件""" self.menu.popup(QtGui.QCursor.pos()) ######################################################################## class MarketMonitor(BasicMonitor): """市场监控组件""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(MarketMonitor, self).__init__(mainEngine, eventEngine, parent) # 设置表头有序字典 d = OrderedDict() d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell} d['lastPrice'] = {'chinese':u'最新价', 'cellType':BasicCell} d['preClosePrice'] = {'chinese':u'昨收盘价', 'cellType':BasicCell} d['volume'] = {'chinese':u'成交量', 'cellType':BasicCell} d['openInterest'] = {'chinese':u'持仓量', 'cellType':BasicCell} d['openPrice'] = {'chinese':u'开盘价', 'cellType':BasicCell} d['highPrice'] = {'chinese':u'最高价', 'cellType':BasicCell} d['lowPrice'] = {'chinese':u'最低价', 'cellType':BasicCell} d['bidPrice1'] = {'chinese':u'买一价', 'cellType':BidCell} d['bidVolume1'] = {'chinese':u'买一量', 'cellType':BidCell} d['askPrice1'] = {'chinese':u'卖一价', 'cellType':AskCell} d['askVolume1'] = {'chinese':u'卖一量', 'cellType':AskCell} d['time'] = {'chinese':u'时间', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) # 设置数据键 self.setDataKey('vtSymbol') # 设置监控事件类型 self.setEventType(EVENT_TICK) # 设置字体 self.setFont(BASIC_FONT) # 设置允许排序 self.setSorting(True) # 初始化表格 self.initTable() # 注册事件监听 self.registerEvent() ######################################################################## class LogMonitor(BasicMonitor): """日志监控""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(LogMonitor, self).__init__(mainEngine, eventEngine, parent) d = OrderedDict() d['logTime'] = {'chinese':u'时间', 'cellType':BasicCell} d['logContent'] = {'chinese':u'内容', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) self.setEventType(EVENT_LOG) self.setFont(BASIC_FONT) self.initTable() self.registerEvent() ######################################################################## class ErrorMonitor(BasicMonitor): """错误监控""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(ErrorMonitor, self).__init__(mainEngine, eventEngine, parent) d = OrderedDict() d['errorTime'] = {'chinese':u'错误时间', 'cellType':BasicCell} d['errorID'] = {'chinese':u'错误代码', 'cellType':BasicCell} d['errorMsg'] = {'chinese':u'错误信息', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) self.setEventType(EVENT_ERROR) self.setFont(BASIC_FONT) self.initTable() self.registerEvent() ######################################################################## class TradeMonitor(BasicMonitor): """成交监控""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(TradeMonitor, self).__init__(mainEngine, eventEngine, parent) d = OrderedDict() d['tradeID'] = {'chinese':u'成交编号', 'cellType':NumCell} d['orderID'] = {'chinese':u'委托编号', 'cellType':NumCell} d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell} d['direction'] = {'chinese':u'方向', 'cellType':DirectionCell} d['offset'] = {'chinese':u'开平', 'cellType':BasicCell} d['price'] = {'chinese':u'价格', 'cellType':BasicCell} d['volume'] = {'chinese':u'数量', 'cellType':BasicCell} d['tradeTime'] = {'chinese':u'成交时间', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) self.setEventType(EVENT_TRADE) self.setFont(BASIC_FONT) self.setSorting(True) self.initTable() self.registerEvent() ######################################################################## class OrderMonitor(BasicMonitor): """委托监控""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(OrderMonitor, self).__init__(mainEngine, eventEngine, parent) self.mainEngine = mainEngine d = OrderedDict() d['orderID'] = {'chinese':u'委托编号', 'cellType':NumCell} d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell} d['direction'] = {'chinese':u'方向', 'cellType':DirectionCell} d['offset'] = {'chinese':u'开平', 'cellType':BasicCell} d['price'] = {'chinese':u'价格', 'cellType':BasicCell} d['totalVolume'] = {'chinese':u'委托数量', 'cellType':BasicCell} d['tradedVolume'] = {'chinese':u'成交数量', 'cellType':BasicCell} d['status'] = {'chinese':u'状态', 'cellType':BasicCell} d['orderTime'] = {'chinese':u'委托时间', 'cellType':BasicCell} d['cancelTime'] = {'chinese':u'撤销时间', 'cellType':BasicCell} d['frontID'] = {'chinese':u'前置编号', 'cellType':BasicCell} d['sessionID'] = {'chinese':u'会话编号', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) self.setDataKey('vtOrderID') self.setEventType(EVENT_ORDER) self.setFont(BASIC_FONT) self.setSaveData(True) self.setSorting(True) self.initTable() self.registerEvent() self.connectSignal() #---------------------------------------------------------------------- def connectSignal(self): """连接信号""" # 双击单元格撤单 self.itemDoubleClicked.connect(self.cancelOrder) #---------------------------------------------------------------------- def cancelOrder(self, cell): """根据单元格的数据撤单""" order = cell.data req = VtCancelOrderReq() req.symbol = order.symbol req.exchange = order.exchange req.frontID = order.frontID req.sessionID = order.sessionID req.orderID = order.orderID self.mainEngine.cancelOrder(req, order.gatewayName) ######################################################################## class PositionMonitor(BasicMonitor): """持仓监控""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(PositionMonitor, self).__init__(mainEngine, eventEngine, parent) d = OrderedDict() d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell} d['direction'] = {'chinese':u'方向', 'cellType':DirectionCell} d['position'] = {'chinese':u'持仓量', 'cellType':BasicCell} d['ydPosition'] = {'chinese':u'昨持仓', 'cellType':BasicCell} d['frozen'] = {'chinese':u'冻结量', 'cellType':BasicCell} d['price'] = {'chinese':u'价格', 'cellType':BasicCell} d['positionProfit'] = {'chinese':u'持仓盈亏', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) self.setDataKey('vtPositionName') self.setEventType(EVENT_POSITION) self.setFont(BASIC_FONT) self.setSaveData(True) self.initTable() self.registerEvent() ######################################################################## class AccountMonitor(BasicMonitor): """账户监控""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(AccountMonitor, self).__init__(mainEngine, eventEngine, parent) d = OrderedDict() d['accountID'] = {'chinese':u'账户', 'cellType':BasicCell} d['preBalance'] = {'chinese':u'昨结', 'cellType':BasicCell} d['balance'] = {'chinese':u'净值', 'cellType':BasicCell} d['available'] = {'chinese':u'可用', 'cellType':BasicCell} d['commission'] = {'chinese':u'手续费', 'cellType':BasicCell} d['margin'] = {'chinese':u'保证金', 'cellType':BasicCell} d['closeProfit'] = {'chinese':u'平仓盈亏', 'cellType':BasicCell} d['positionProfit'] = {'chinese':u'持仓盈亏', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) self.setDataKey('vtAccountID') self.setEventType(EVENT_ACCOUNT) self.setFont(BASIC_FONT) self.initTable() self.registerEvent() ######################################################################## class TradingWidget(QtGui.QFrame): """简单交易组件""" signal = QtCore.pyqtSignal(type(Event())) directionList = [DIRECTION_LONG, DIRECTION_SHORT] offsetList = [OFFSET_OPEN, OFFSET_CLOSE, OFFSET_CLOSEYESTERDAY, OFFSET_CLOSETODAY] priceTypeList = [PRICETYPE_LIMITPRICE, PRICETYPE_MARKETPRICE, PRICETYPE_FAK, PRICETYPE_FOK] exchangeList = [EXCHANGE_NONE, EXCHANGE_CFFEX, EXCHANGE_SHFE, EXCHANGE_DCE, EXCHANGE_CZCE, EXCHANGE_SSE, EXCHANGE_SZSE, EXCHANGE_SGE, EXCHANGE_HKEX, EXCHANGE_HKFE, EXCHANGE_SMART, EXCHANGE_ICE, EXCHANGE_CME, EXCHANGE_NYMEX, EXCHANGE_GLOBEX, EXCHANGE_IDEALPRO] currencyList = [CURRENCY_NONE, CURRENCY_CNY, CURRENCY_HKD, CURRENCY_USD] productClassList = [PRODUCT_NONE, PRODUCT_EQUITY, PRODUCT_FUTURES, PRODUCT_OPTION, PRODUCT_FOREX] gatewayList = [''] #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(TradingWidget, self).__init__(parent) self.mainEngine = mainEngine self.eventEngine = eventEngine self.symbol = '' # 添加交易接口 self.gatewayList.extend(mainEngine.getAllGatewayNames()) self.initUi() self.connectSignal() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'交易') self.setMaximumWidth(400) self.setFrameShape(self.Box) # 设置边框 self.setLineWidth(1) # 左边部分 labelSymbol = QtGui.QLabel(u'代码') labelName = QtGui.QLabel(u'名称') labelDirection = QtGui.QLabel(u'方向类型') labelOffset = QtGui.QLabel(u'开平') labelPrice = QtGui.QLabel(u'价格') self.checkFixed = QtGui.QCheckBox(u'') # 价格固定选择框 labelVolume = QtGui.QLabel(u'数量') labelPriceType = QtGui.QLabel(u'价格类型') labelExchange = QtGui.QLabel(u'交易所') labelCurrency = QtGui.QLabel(u'货币') labelProductClass = QtGui.QLabel(u'产品类型') labelGateway = QtGui.QLabel(u'交易接口') self.lineSymbol = QtGui.QLineEdit() self.lineName = QtGui.QLineEdit() self.comboDirection = QtGui.QComboBox() self.comboDirection.addItems(self.directionList) self.comboOffset = QtGui.QComboBox() self.comboOffset.addItems(self.offsetList) self.spinPrice = QtGui.QDoubleSpinBox() self.spinPrice.setDecimals(4) self.spinPrice.setMinimum(0) self.spinPrice.setMaximum(100000) self.spinVolume = QtGui.QSpinBox() self.spinVolume.setMinimum(0) self.spinVolume.setMaximum(1000000) self.comboPriceType = QtGui.QComboBox() self.comboPriceType.addItems(self.priceTypeList) self.comboExchange = QtGui.QComboBox() self.comboExchange.addItems(self.exchangeList) self.comboCurrency = QtGui.QComboBox() self.comboCurrency.addItems(self.currencyList) self.comboProductClass = QtGui.QComboBox() self.comboProductClass.addItems(self.productClassList) self.comboGateway = QtGui.QComboBox() self.comboGateway.addItems(self.gatewayList) gridleft = QtGui.QGridLayout() gridleft.addWidget(labelSymbol, 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(labelExchange, 7, 0) gridleft.addWidget(labelCurrency, 8, 0) gridleft.addWidget(labelProductClass, 9, 0) gridleft.addWidget(labelGateway, 10, 0) gridleft.addWidget(self.lineSymbol, 0, 1, 1, -1) gridleft.addWidget(self.lineName, 1, 1, 1, -1) gridleft.addWidget(self.comboDirection, 2, 1, 1, -1) gridleft.addWidget(self.comboOffset, 3, 1, 1, -1) gridleft.addWidget(self.checkFixed, 4, 1) gridleft.addWidget(self.spinPrice, 4, 2) gridleft.addWidget(self.spinVolume, 5, 1, 1, -1) gridleft.addWidget(self.comboPriceType, 6, 1, 1, -1) gridleft.addWidget(self.comboExchange, 7, 1, 1, -1) gridleft.addWidget(self.comboCurrency, 8, 1, 1, -1) gridleft.addWidget(self.comboProductClass, 9, 1, 1, -1) gridleft.addWidget(self.comboGateway, 10, 1, 1, -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'全撤') size = buttonSendOrder.sizeHint() buttonSendOrder.setMinimumHeight(size.height()*2) # 把按钮高度设为默认两倍 buttonCancelAll.setMinimumHeight(size.height()*2) # 整合布局 hbox = QtGui.QHBoxLayout() hbox.addLayout(gridleft) hbox.addLayout(gridRight) vbox = QtGui.QVBoxLayout() vbox.addLayout(hbox) vbox.addWidget(buttonSendOrder) vbox.addWidget(buttonCancelAll) vbox.addStretch() self.setLayout(vbox) # 关联更新 buttonSendOrder.clicked.connect(self.sendOrder) buttonCancelAll.clicked.connect(self.cancelAll) self.lineSymbol.returnPressed.connect(self.updateSymbol) #---------------------------------------------------------------------- def updateSymbol(self): """合约变化""" # 读取组件数据 symbol = str(self.lineSymbol.text()) exchange = unicode(self.comboExchange.currentText()) currency = unicode(self.comboCurrency.currentText()) productClass = unicode(self.comboProductClass.currentText()) gatewayName = unicode(self.comboGateway.currentText()) # 查询合约 if exchange: vtSymbol = '.'.join([symbol, exchange]) contract = self.mainEngine.getContract(vtSymbol) else: vtSymbol = symbol contract = self.mainEngine.getContract(symbol) if contract: vtSymbol = contract.vtSymbol gatewayName = contract.gatewayName self.lineName.setText(contract.name) exchange = contract.exchange # 保证有交易所代码 # 清空价格数量 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_TICK + self.symbol, self.signal.emit) self.eventEngine.register(EVENT_TICK + vtSymbol, self.signal.emit) # 订阅合约 req = VtSubscribeReq() req.symbol = symbol req.exchange = exchange req.currency = currency req.productClass = productClass # 默认跟随价 self.checkFixed.setChecked(False) self.mainEngine.subscribe(req, gatewayName) # 更新组件当前交易的合约 self.symbol = vtSymbol #---------------------------------------------------------------------- def updateTick(self, event): """更新行情""" tick = event.dict_['data'] if tick.vtSymbol == self.symbol: if not self.checkFixed.isChecked(): self.spinPrice.setValue(tick.lastPrice) self.labelBidPrice1.setText(str(tick.bidPrice1)) self.labelAskPrice1.setText(str(tick.askPrice1)) self.labelBidVolume1.setText(str(tick.bidVolume1)) self.labelAskVolume1.setText(str(tick.askVolume1)) if tick.bidPrice2: self.labelBidPrice2.setText(str(tick.bidPrice2)) self.labelBidPrice3.setText(str(tick.bidPrice3)) self.labelBidPrice4.setText(str(tick.bidPrice4)) self.labelBidPrice5.setText(str(tick.bidPrice5)) self.labelAskPrice2.setText(str(tick.askPrice2)) self.labelAskPrice3.setText(str(tick.askPrice3)) self.labelAskPrice4.setText(str(tick.askPrice4)) self.labelAskPrice5.setText(str(tick.askPrice5)) self.labelBidVolume2.setText(str(tick.bidVolume2)) self.labelBidVolume3.setText(str(tick.bidVolume3)) self.labelBidVolume4.setText(str(tick.bidVolume4)) self.labelBidVolume5.setText(str(tick.bidVolume5)) self.labelAskVolume2.setText(str(tick.askVolume2)) self.labelAskVolume3.setText(str(tick.askVolume3)) self.labelAskVolume4.setText(str(tick.askVolume4)) self.labelAskVolume5.setText(str(tick.askVolume5)) self.labelLastPrice.setText(str(tick.lastPrice)) if tick.preClosePrice: rt = (tick.lastPrice/tick.preClosePrice)-1 self.labelReturn.setText(('%.2f' %(rt*100))+'%') else: self.labelReturn.setText('') #---------------------------------------------------------------------- def connectSignal(self): """连接Signal""" self.signal.connect(self.updateTick) #---------------------------------------------------------------------- def sendOrder(self): """发单""" symbol = str(self.lineSymbol.text()) exchange = unicode(self.comboExchange.currentText()) currency = unicode(self.comboCurrency.currentText()) productClass = unicode(self.comboProductClass.currentText()) gatewayName = unicode(self.comboGateway.currentText()) # 查询合约 if exchange: vtSymbol = '.'.join([symbol, exchange]) contract = self.mainEngine.getContract(vtSymbol) else: vtSymbol = symbol contract = self.mainEngine.getContract(symbol) if contract: gatewayName = contract.gatewayName exchange = contract.exchange # 保证有交易所代码 req = VtOrderReq() req.symbol = symbol req.exchange = exchange req.price = self.spinPrice.value() req.volume = self.spinVolume.value() req.direction = unicode(self.comboDirection.currentText()) req.priceType = unicode(self.comboPriceType.currentText()) req.offset = unicode(self.comboOffset.currentText()) req.currency = currency req.productClass = productClass self.mainEngine.sendOrder(req, gatewayName) #---------------------------------------------------------------------- def cancelAll(self): """一键撤销所有委托""" l = self.mainEngine.getAllWorkingOrders() for order in l: req = VtCancelOrderReq() req.symbol = order.symbol req.exchange = order.exchange req.frontID = order.frontID req.sessionID = order.sessionID req.orderID = order.orderID self.mainEngine.cancelOrder(req, order.gatewayName) #---------------------------------------------------------------------- def closePosition(self, cell): """根据持仓信息自动填写交易组件""" # 读取持仓数据,cell是一个表格中的单元格对象 pos = cell.data symbol = pos.symbol # 更新交易组件的显示合约 self.lineSymbol.setText(symbol) self.updateSymbol() # 自动填写信息 self.comboPriceType.setCurrentIndex(self.priceTypeList.index(PRICETYPE_LIMITPRICE)) self.comboOffset.setCurrentIndex(self.offsetList.index(OFFSET_CLOSE)) self.spinVolume.setValue(pos.position) if pos.direction == DIRECTION_LONG or pos.direction == DIRECTION_NET: self.comboDirection.setCurrentIndex(self.directionList.index(DIRECTION_SHORT)) else: self.comboDirection.setCurrentIndex(self.directionList.index(DIRECTION_LONG)) # 价格留待更新后由用户输入,防止有误操作 ######################################################################## class ContractMonitor(BasicMonitor): """合约查询""" #---------------------------------------------------------------------- def __init__(self, mainEngine, parent=None): """Constructor""" super(ContractMonitor, self).__init__(parent=parent) self.mainEngine = mainEngine d = OrderedDict() d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} d['exchange'] = {'chinese':u'交易所', 'cellType':BasicCell} d['vtSymbol'] = {'chinese':u'vt系统代码', 'cellType':BasicCell} d['name'] = {'chinese':u'名称', 'cellType':BasicCell} d['productClass'] = {'chinese':u'合约类型', 'cellType':BasicCell} d['size'] = {'chinese':u'大小', 'cellType':BasicCell} d['priceTick'] = {'chinese':u'最小价格变动', 'cellType':BasicCell} #d['strikePrice'] = {'chinese':u'期权行权价', 'cellType':BasicCell} #d['underlyingSymbol'] = {'chinese':u'期权标的物', 'cellType':BasicCell} #d['optionType'] = {'chinese':u'期权类型', 'cellType':BasicCell} self.setHeaderDict(d) # 过滤显示用的字符串 self.filterContent = EMPTY_STRING self.initUi() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'合约查询') self.setMinimumSize(800, 800) self.setFont(BASIC_FONT) self.initTable() self.addMenuAction() #---------------------------------------------------------------------- def showAllContracts(self): """显示所有合约数据""" l = self.mainEngine.getAllContracts() d = {'.'.join([contract.exchange, contract.symbol]):contract for contract in l} l2 = d.keys() l2.sort(reverse=True) self.setRowCount(len(l2)) row = 0 for key in l2: # 如果设置了过滤信息且合约代码中不含过滤信息,则不显示 if self.filterContent and self.filterContent not in key: continue contract = d[key] for n, header in enumerate(self.headerList): content = safeUnicode(contract.__getattribute__(header)) cellType = self.headerDict[header]['cellType'] cell = cellType(content) if self.font: cell.setFont(self.font) # 如果设置了特殊字体,则进行单元格设置 self.setItem(row, n, cell) row = row + 1 #---------------------------------------------------------------------- def refresh(self): """刷新""" self.menu.close() # 关闭菜单 self.clearContents() self.setRowCount(0) self.showAllContracts() #---------------------------------------------------------------------- def addMenuAction(self): """增加右键菜单内容""" refreshAction = QtGui.QAction(u'刷新', self) refreshAction.triggered.connect(self.refresh) self.menu.addAction(refreshAction) #---------------------------------------------------------------------- def show(self): """显示""" super(ContractMonitor, self).show() self.refresh() #---------------------------------------------------------------------- def setFilterContent(self, content): """设置过滤字符串""" self.filterContent = content ######################################################################## class ContractManager(QtGui.QWidget): """合约管理组件""" #---------------------------------------------------------------------- def __init__(self, mainEngine, parent=None): """Constructor""" super(ContractManager, self).__init__(parent=parent) self.mainEngine = mainEngine self.initUi() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'合约查询') self.lineFilter = QtGui.QLineEdit() self.buttonFilter = QtGui.QPushButton(u'查询') self.buttonFilter.clicked.connect(self.filterContract) self.monitor = ContractMonitor(self.mainEngine) self.monitor.refresh() hbox = QtGui.QHBoxLayout() hbox.addWidget(self.lineFilter) hbox.addWidget(self.buttonFilter) hbox.addStretch() vbox = QtGui.QVBoxLayout() vbox.addLayout(hbox) vbox.addWidget(self.monitor) self.setLayout(vbox) #---------------------------------------------------------------------- def filterContract(self): """显示过滤后的合约""" content = str(self.lineFilter.text()) self.monitor.setFilterContent(content) self.monitor.refresh()