# encoding: UTF-8 # import json from __future__ import division import csv from builtins import range from builtins import str from collections import OrderedDict from past.utils import old_div # PyQt 4/5 compatibility try: from PyQt4.QtGui import QTableWidgetItem, QTableWidget, QFrame, QLabel, QLineEdit, QComboBox, QDoubleSpinBox, \ QSpinBox, QCheckBox, QGridLayout, QHBoxLayout, QVBoxLayout, QPushButton, QMenu, QAction, QHeaderView from PyQt4 import QtGui, QtCore except ImportError: from PyQt5 import QtGui, QtCore from PyQt5.QtWidgets import QTableWidgetItem, QTableWidget, QFrame, QLabel, QLineEdit, QComboBox, QDoubleSpinBox, \ QSpinBox, QCheckBox, QGridLayout, QHBoxLayout, QVBoxLayout, QPushButton, QMenu, QAction, QHeaderView from eventEngine import * from vtFunction import * from vtGateway import * # ---------------------------------------------------------------------- def loadFont(): """载入字体设置""" try: f = file("setting/VT_setting.json") 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(QTableWidgetItem): """基础的单元格""" # ---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None, foreground=None, background=None): """Constructor""" super(BasicCell, self).__init__() if foreground is not None: self.setForeground(foreground) if background is not None: self.setBackground(background) self.data = None if text: self.setContent(text) def _conditional_style_before(self, text): pass def _conditional_style_after(self, text): pass def _after_set_text(self): pass def _get_text(self, text): return text # ---------------------------------------------------------------------- def setContent(self, text): """设置内容""" if text is None: self.setText("") return real_text = self._get_text(text) self._conditional_style_before(text) self.setText(real_text) self._conditional_style_after(text) self._after_set_text() class ErrorCell(BasicCell): """基础的单元格""" def _conditional_style_after(self, text): if text.find(u'失败') >= 0 or text.find(u'错误') >= 0: self.setForeground(QtGui.QColor('red')) self.setToolTip(text) ######################################################################## class NumCell(BasicCell): """用来显示数字的单元格""" def _get_text(self, text): if text == '0' or text == '0.0': res = '' else: res = text return res ######################################################################## class NumCellColored(NumCell): """用来显示数字的单元格""" def _conditional_style_after(self, text): try: num = float(text) except Exception as e: print(type(text), text) raise(e) if num < 0: self.setForeground(QtGui.QColor('green')) else: self.setForeground(QtGui.QColor('red')) class PositionCell(NumCell): """用来显示数字的单元格""" pass ######################################################################## class DirectionCell(BasicCell): """用来显示买卖方向的单元格""" # ---------------------------------------------------------------------- def _conditional_style_after(self, text): """设置内容""" if text == DIRECTION_LONG or text == DIRECTION_NET: self.setForeground(QtGui.QColor('red')) elif text == DIRECTION_SHORT: self.setForeground(QtGui.QColor('green')) ######################################################################## class NameCell(BasicCell): """用来显示合约中文的单元格""" # ---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" self.mainEngine = mainEngine super(NameCell, self).__init__(text=text, mainEngine=mainEngine) def _get_text(self, text): if self.mainEngine: # 首先尝试正常获取合约对象 contract = self.mainEngine.getContract(text) # 如果能读取合约信息 if contract: return contract.name ######################################################################## class BidCell(BasicCell): """买价单元格""" # ---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(BidCell, self).__init__(text=text, mainEngine=mainEngine, background=QtGui.QColor(255, 174, 201), foreground=QtGui.QColor('black')) ######################################################################## class AskCell(BasicCell): """买价单元格""" # ---------------------------------------------------------------------- def __init__(self, text=None, mainEngine=None): """Constructor""" super(AskCell, self).__init__(text=text, mainEngine=mainEngine, background=QtGui.QColor(160, 255, 160), foreground=QtGui.QColor('black')) ######################################################################## class BasicMonitor(QTableWidget): """ 基础监控 headerDict中的值对应的字典格式如下 {'chinese': u'中文名', 'cellType': BasicCell} """ signal = QtCore.pyqtSignal(type(Event())) signal_clear = 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.signalemit = None self.signalclearemit = None # 保存表头标签用 self.headerDict = OrderedDict() # 有序字典,key是英文名,value是对应的配置字典 self.headerList = [] # 对应self.headerDict.keys() # 保存相关数据用 self.dataDict = {} # 字典,key是字段对应的数据,value是保存相关单元格的字典 self.dataKey = '' # 字典键对应的数据字段 # 监控的事件类型 self.eventType = '' self.eventTypeClear = '' # 字体 self.font = None # 保存数据对象到单元格 self.saveData = False # 默认不允许根据表头进行排序,需要的组件可以开启 self.sorting = False # 初始化右键菜单 self.initMenu() # ---------------------------------------------------------------------- def setHeaderDict(self, headerDict): """设置表头有序字典""" self.headerDict = headerDict self.headerList = list(headerDict.keys()) # ---------------------------------------------------------------------- def setDataKey(self, dataKey): """设置数据字典的键""" self.dataKey = dataKey # ---------------------------------------------------------------------- def setEventType(self, eventType1, eventType2=''): """设置监控的事件类型""" self.eventType = eventType1 self.eventTypeClear = eventType2 # ---------------------------------------------------------------------- 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 list(self.headerDict.values())] self.setHorizontalHeaderLabels(labels) # 关闭左边的垂直表头 self.verticalHeader().setVisible(False) # 设为不可编辑 self.setEditTriggers(self.NoEditTriggers) # 设为行交替颜色 self.setAlternatingRowColors(True) # 设置允许排序 self.setSortingEnabled(self.sorting) # set stretch # self.horizontalHeader().setResizeMode(QHeaderView.Stretch) # ---------------------------------------------------------------------- def registerEvent(self): """注册GUI更新相关的事件监听""" self.signal.connect(self.updateEvent) self.signalemit = self.signal.emit self.eventEngine.register(self.eventType, self.signalemit) if len(self.eventTypeClear) > 0: self.signal_clear.connect(self.clearEvent) self.signalclearemit = self.signal_clear.emit self.eventEngine.register(self.eventTypeClear, self.signalclearemit) def unRegister(self): self.eventEngine.unregister(self.eventType, self.signalemit) if len(self.eventTypeClear) > 0: self.eventEngine.unregister(self.eventTypeClear, self.signalclearemit) # ---------------------------------------------------------------------- def updateEvent(self, event): """收到事件更新""" data = event.dict_['data'] self.updateData(data) def clearEvent(self, event): """收到事件更新""" self.dataDict.clear() self.setRowCount(0) # ---------------------------------------------------------------------- def updateData(self, data): """将数据更新到表格中""" # 如果允许了排序功能,则插入数据前必须关闭,否则插入新的数据会变乱 if self.sorting: self.setSortingEnabled(False) # 如果设置了dataKey,则采用存量更新模式 if self.dataKey: #key = data.__getattribute__(self.dataKey) key = getattr(data, self.dataKey, None) # 如果键在数据字典中不存在,则先插入新的一行,并创建对应单元格 if key not in self.dataDict: self.insertRow(0) d = {} for n, header in enumerate(self.headerList): content = safeUnicode(data.__getattribute__(header)) # content = safeUnicode(getattr(data, header, None)) cellType = self.headerDict[header]['cellType'] try: cell = cellType(content, self.mainEngine) except Exception as e: print(cellType) print(data) print(content) raise(e) 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(getattr(data, header, None)) 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(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(safeUnicode(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( safeUnicode(item.text()).encode('gbk')) else: rowdata.append('') writer.writerow(rowdata) except IOError: pass # ---------------------------------------------------------------------- def initMenu(self): """初始化右键菜单""" self.menu = QMenu(self) ''' saveAction = QAction(u'保存内容', self) saveAction.triggered.connect(self.saveToCsv) resizeAction = QAction(u'调整列宽', self) resizeAction.triggered.connect(self.resizeColumns) self.menu.addAction(resizeAction) 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['name'] = {'chinese': u'名称', 'cellType': NameCell} d['lastPrice'] = {'chinese': u'最新价', 'cellType': BasicCell} d['preClosePrice'] = {'chinese': u'昨收盘价', 'cellType': BasicCell} d['volume'] = {'chinese': u'成交量', 'cellType': NumCell} d['openInterest'] = {'chinese': u'持仓量', 'cellType': NumCell} 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': NumCell} d['askPrice1'] = {'chinese': u'卖一价', 'cellType': AskCell} d['askVolume1'] = {'chinese': u'卖一量', 'cellType': NumCell} d['time'] = {'chinese': u'时间', 'cellType': BasicCell} d['volchg'] = {'chinese': u'成交量变', 'cellType': NumCell} d['turnover'] = {'chinese': u'成交额', 'cellType': BasicCell} # d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) # 设置数据键 self.setDataKey('symbol') # 设置监控事件类型 self.setEventType(EVENT_TICK, EVENT_CLEAR) # 设置字体 self.setFont(BASIC_FONT) # 设置允许排序 self.setSorting(False) self.setSaveData(True) # 初始化表格 self.initTable() # 注册事件监听 self.registerEvent() def updateEvent(self, event): new_tick = event.dict_['data'] if new_tick.symbol in self.dataDict: old_tick = self.dataDict[new_tick.symbol]['symbol'].data new_tick.volchg = new_tick.volume - old_tick.volume if new_tick.volume > old_tick.volume else old_tick.volchg BasicMonitor.updateEvent(self, event) ######################################################################## 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': ErrorCell} 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['additionalInfo'] = {'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': BasicCell} d['orderID'] = {'chinese': u'委托编号', 'cellType': BasicCell} d['symbol'] = {'chinese': u'合约代码', 'cellType': BasicCell} d['name'] = {'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': NumCell} d['tradeTime'] = {'chinese': u'成交时间', 'cellType': BasicCell} # d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) self.setDataKey('vtTradeID') self.setEventType(EVENT_TRADE, EVENT_CLEAR) self.setFont(BASIC_FONT) self.setSorting(True) self.initTable() self.registerEvent() ######################################################################## class OrderMonitor(BasicMonitor): """委托监控""" signal_trade = QtCore.pyqtSignal(type(Event())) # ---------------------------------------------------------------------- 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': BasicCell} d['symbol'] = {'chinese': u'合约代码', 'cellType': BasicCell} d['name'] = {'chinese': u'名称', 'cellType': NameCell} d['priceType'] = {'chinese': u'算法', 'cellType': BasicCell} d['direction'] = {'chinese': u'方向', 'cellType': DirectionCell} d['offset'] = {'chinese': u'开平', 'cellType': BasicCell} d['price'] = {'chinese': u'价格', 'cellType': BasicCell} d['totalVolume'] = {'chinese': u'委托数量', 'cellType': NumCell} d['tradePrice'] = {'chinese': u'成交价格', 'cellType': BasicCell} d['tradedVolume'] = {'chinese': u'成交数量', 'cellType': NumCell} d['status'] = {'chinese': u'状态', 'cellType': BasicCell} d['orderTime'] = {'chinese': u'委托时间', 'cellType': BasicCell} d['taskID'] = {'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, EVENT_CLEAR) self.setFont(BASIC_FONT) self.setSaveData(True) self.setSorting(True) self.initTable() self.registerEvent() self.connectSignal() self.registerTradeEvent() def registerTradeEvent(self): self.signal_trade.connect(self.updateTrade) self.eventEngine.register(EVENT_TRADE, self.signal_trade.emit) # ---------------------------------------------------------------------- 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.taskID self.mainEngine.cancelOrder(req, order.gatewayName) def updateTrade(self, event): """更新成交数据""" trade = event.dict_['data'] if trade.vtOrderID in self.dataDict: order = self.dataDict[trade.vtOrderID]['orderID'].data if order.status not in (STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED): order.tradePrice = old_div((trade.volume * trade.price + order.tradePrice * order.tradedVolume), (order.tradedVolume + trade.volume)) order.tradedVolume += trade.volume self.updateData(order) ######################################################################## 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['name'] = {'chinese': u'名称', 'cellType': NameCell} d['direction'] = {'chinese': u'方向', 'cellType': DirectionCell} d['initPosition'] = {'chinese': u'初持仓', 'cellType': PositionCell} d['position'] = {'chinese': u'总持仓', 'cellType': PositionCell} d['price'] = {'chinese': u'成本价', 'cellType': NumCell} d['ydPosition'] = {'chinese': u'昨持仓', 'cellType': PositionCell} d['tdPosition'] = {'chinese': u'今持仓', 'cellType': PositionCell} d['frozen'] = {'chinese': u'冻结量', 'cellType': NumCell} d['enable'] = {'chinese': u'可用量', 'cellType': NumCell} d['want'] = {'chinese': u'开仓在途', 'cellType': NumCell} d['trading'] = {'chinese': u'交易赢亏', 'cellType': NumCellColored} d['holding'] = {'chinese': u'持仓赢亏', 'cellType': NumCellColored} d['commission'] = {'chinese': u'手续费', 'cellType': NumCell} d['last'] = {'chinese': u'市场价', 'cellType': NumCell} d['mktval'] = {'chinese': u'市值', 'cellType': NumCell} self.setHeaderDict(d) self.setDataKey('vtPositionName') self.setEventType(EVENT_POSITION, EVENT_CLEAR) self.setFont(BASIC_FONT) self.setSaveData(True) self.setSorting(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['type'] = {'chinese': u'类型', 'cellType': BasicCell} d['init_balance'] = {'chinese': u'期初', 'cellType': NumCell} d['enable_balance'] = {'chinese': u'可用', 'cellType': NumCell} d['frozen_balance'] = {'chinese': u'冻结', 'cellType': NumCell} d['margin'] = {'chinese': u'保证金', 'cellType': NumCell} d['holding_pnl'] = {'chinese': u'持仓盈亏', 'cellType': NumCellColored} d['trading_pnl'] = {'chinese': u'交易盈亏', 'cellType': NumCellColored} d['commission'] = {'chinese': u'手续费', 'cellType': NumCell} d['float_pnl'] = {'chinese': u'浮动盈亏', 'cellType': NumCellColored} d['close_pnl'] = {'chinese': u'平仓盈亏', 'cellType': NumCellColored} d['deposit_balance'] = {'chinese': u'差额', 'cellType': NumCell} self.setHeaderDict(d) self.setDataKey('vtAccountID') self.setEventType(EVENT_ACCOUNT, EVENT_CLEAR) self.setFont(BASIC_FONT) self.initTable() self.registerEvent() ######################################################################## class TradingWidget(QFrame): """简单交易组件""" signal = QtCore.pyqtSignal(type(Event())) directionList = [DIRECTION_LONG, DIRECTION_SHORT] offsetList = [OFFSET_OPEN, OFFSET_CLOSE, OFFSET_CLOSEYESTERDAY, OFFSET_CLOSETODAY] priceTypeList = [PRICETYPE_LIMITPRICE, PRICETYPE_VWAP, PRICETYPE_TWAP] exchangeList = [EXCHANGE_NONE, EXCHANGE_CFFEX, EXCHANGE_SHFE, EXCHANGE_DCE, EXCHANGE_CZCE, EXCHANGE_SSE, EXCHANGE_SZSE, EXCHANGE_SGE, EXCHANGE_HKEX, EXCHANGE_CSI, EXCHANGE_HKH, EXCHANGE_HKS, EXCHANGE_JZ, EXCHANGE_SPOT, EXCHANGE_IB, EXCHANGE_FX, EXCHANGE_INE, EXCHANGE_SMART, EXCHANGE_ICE, EXCHANGE_CME, EXCHANGE_NYMEX, EXCHANGE_GLOBEX, EXCHANGE_IDEALPRO] currencyList = [CURRENCY_NONE, CURRENCY_CNY, CURRENCY_USD] productClassList = [PRODUCT_NONE, PRODUCT_EQUITY, PRODUCT_FUTURES, PRODUCT_OPTION, PRODUCT_BOND, PRODUCT_FOREX] # ---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(TradingWidget, self).__init__(parent) self.mainEngine = mainEngine self.eventEngine = eventEngine self.symbol = '' self.signalemit = None self.initUi() self.connectSignal() # ---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'交易') self.setMaximumWidth(500) self.setFrameShape(self.Box) # 设置边框 self.setLineWidth(1) # 左边部分 labelSymbol = QLabel(u'代码') labelName = QLabel(u'名称') labelDirection = QLabel(u'方向类型') labelOffset = QLabel(u'开平') labelPrice = QLabel(u'价格') labelVolume = QLabel(u'数量') labelPriceType = QLabel(u'价格类型') labelExchange = QLabel(u'交易所') labelCurrency = QLabel(u'货币') labelProductClass = QLabel(u'产品类型') labelUrgency = QLabel(u'紧急度') self.lineSymbol = QLineEdit() self.lineName = QLineEdit() self.comboDirection = QComboBox() self.comboDirection.addItems(self.directionList) self.comboOffset = QComboBox() self.comboOffset.addItem('') self.comboOffset.addItems(self.offsetList) self.comboOffset.setEnabled(False) self.tickOffset = QCheckBox(u'指定') self.spinPrice = QDoubleSpinBox() self.spinPrice.setDecimals(4) self.spinPrice.setMinimum(0) self.spinPrice.setMaximum(500000) self.spinVolume = QSpinBox() self.spinVolume.setMinimum(0) self.spinVolume.setMaximum(1000000) self.comboPriceType = QComboBox() self.comboPriceType.addItems(self.priceTypeList) self.comboExchange = QComboBox() self.comboExchange.addItems(self.exchangeList) self.comboExchange.setEnabled(False) self.comboCurrency = QComboBox() self.comboCurrency.addItems(self.currencyList) self.comboCurrency.setEnabled(False) self.comboProductClass = QComboBox() self.comboProductClass.addItems(self.productClassList) self.comboProductClass.setEnabled(False) self.spinUrgency = QSpinBox() self.spinUrgency.setMinimum(1) self.spinUrgency.setMaximum(9) self.spinUrgency.setSingleStep(1) self.spinUrgency.setValue(5) gridleft = 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(labelUrgency, 7, 0) gridleft.addWidget(labelExchange, 8, 0) gridleft.addWidget(labelProductClass, 9, 0) gridleft.addWidget(labelCurrency, 10, 0) gridleft.addWidget(self.lineSymbol, 0, 1) gridleft.addWidget(self.lineName, 1, 1) gridleft.addWidget(self.comboDirection, 2, 1) hbox1 = QHBoxLayout() hbox1.addWidget(self.comboOffset) lable1 = QLabel() hbox1.addWidget(lable1) hbox1.addWidget(self.tickOffset) hbox1.setStretchFactor(self.comboOffset, 4) hbox1.setStretchFactor(lable1, 1) hbox1.setStretchFactor(self.tickOffset, 3) gridleft.addItem(hbox1, 3, 1) gridleft.addWidget(self.spinPrice, 4, 1) gridleft.addWidget(self.spinVolume, 5, 1) gridleft.addWidget(self.comboPriceType, 6, 1) gridleft.addWidget(self.spinUrgency, 7, 1) gridleft.addWidget(self.comboExchange, 8, 1) gridleft.addWidget(self.comboProductClass, 9, 1) gridleft.addWidget(self.comboCurrency, 10, 1) # 右边部分 labelBid1 = QLabel(u'买一') labelBid2 = QLabel(u'买二') labelBid3 = QLabel(u'买三') labelBid4 = QLabel(u'买四') labelBid5 = QLabel(u'买五') labelAsk1 = QLabel(u'卖一') labelAsk2 = QLabel(u'卖二') labelAsk3 = QLabel(u'卖三') labelAsk4 = QLabel(u'卖四') labelAsk5 = QLabel(u'卖五') self.labelBidPrice1 = QLabel() self.labelBidPrice2 = QLabel() self.labelBidPrice3 = QLabel() self.labelBidPrice4 = QLabel() self.labelBidPrice5 = QLabel() self.labelBidVolume1 = QLabel() self.labelBidVolume2 = QLabel() self.labelBidVolume3 = QLabel() self.labelBidVolume4 = QLabel() self.labelBidVolume5 = QLabel() self.labelAskPrice1 = QLabel() self.labelAskPrice2 = QLabel() self.labelAskPrice3 = QLabel() self.labelAskPrice4 = QLabel() self.labelAskPrice5 = QLabel() self.labelAskVolume1 = QLabel() self.labelAskVolume2 = QLabel() self.labelAskVolume3 = QLabel() self.labelAskVolume4 = QLabel() self.labelAskVolume5 = QLabel() labelLast = QLabel(u'最新') self.labelLastPrice = QLabel() self.labelReturn = QLabel() self.labelLastPrice.setMinimumWidth(60) self.labelReturn.setMinimumWidth(60) gridRight = 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 = QPushButton(u'发单') buttonCancelAll = QPushButton(u'全撤') size = buttonSendOrder.sizeHint() buttonSendOrder.setMinimumHeight(size.height() * 2) # 把按钮高度设为默认两倍 buttonCancelAll.setMinimumHeight(size.height() * 2) # 整合布局 hbox = QHBoxLayout() hbox.addLayout(gridleft) hbox.addLayout(gridRight) vbox = 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) self.comboDirection.currentIndexChanged.connect(self.updateOffset) self.tickOffset.stateChanged.connect(self.updateOffset) self.labelAskPrice1.mouseDoubleClickEvent = self.ask1clicked self.labelAskPrice2.mouseDoubleClickEvent = self.ask2clicked self.labelAskPrice3.mouseDoubleClickEvent = self.ask3clicked self.labelAskPrice4.mouseDoubleClickEvent = self.ask4clicked self.labelAskPrice5.mouseDoubleClickEvent = self.ask5clicked self.labelBidPrice1.mouseDoubleClickEvent = self.bid1clicked self.labelBidPrice2.mouseDoubleClickEvent = self.bid2clicked self.labelBidPrice3.mouseDoubleClickEvent = self.bid3clicked self.labelBidPrice4.mouseDoubleClickEvent = self.bid4clicked self.labelBidPrice5.mouseDoubleClickEvent = self.bid5clicked self.labelLastPrice.mouseDoubleClickEvent = self.lastclicked def ask1clicked(self, a): self.askclicked(self.labelAskPrice1.text()) def ask2clicked(self, a): self.askclicked(self.labelAskPrice2.text()) def ask3clicked(self, a): self.askclicked(self.labelAskPrice3.text()) def ask4clicked(self, a): self.askclicked(self.labelAskPrice4.text()) def ask5clicked(self, a): self.askclicked(self.labelAskPrice5.text()) def bid1clicked(self, a): self.bidclicked(self.labelBidPrice1.text()) def bid2clicked(self, a): self.bidclicked(self.labelBidPrice2.text()) def bid3clicked(self, a): self.bidclicked(self.labelBidPrice3.text()) def bid4clicked(self, a): self.bidclicked(self.labelBidPrice4.text()) def bid5clicked(self, a): self.bidclicked(self.labelBidPrice5.text()) def lastclicked(self, a): self.setPrice(self.labelLastPrice.text()) def setPrice(self, text): result = False if text is not None and len(text) > 0: price = float(str(text)) if price > 0: self.spinPrice.setValue(price) result = True return result def askclicked(self, text): if self.setPrice(text): self.comboDirection.setCurrentIndex(self.directionList.index(DIRECTION_LONG)) self.updateOffset() def bidclicked(self, text): if self.setPrice(text): self.comboDirection.setCurrentIndex(self.directionList.index(DIRECTION_SHORT)) self.updateOffset() def updateOffset(self): if self.tickOffset.checkState(): self.comboOffset.setEnabled(True) if self.comboProductClass.currentText() in (PRODUCT_EQUITY, PRODUCT_BOND): dir = self.comboDirection.currentText() if dir == DIRECTION_LONG: self.comboOffset.setCurrentIndex(self.offsetList.index(OFFSET_OPEN) + 1) elif dir == DIRECTION_SHORT: self.comboOffset.setCurrentIndex(self.offsetList.index(OFFSET_CLOSE) + 1) elif self.comboOffset.currentIndex() == 0: self.comboOffset.setCurrentIndex(1) else: self.comboOffset.setEnabled(False) self.comboOffset.setCurrentIndex(0) # ---------------------------------------------------------------------- def updateSymbol(self): """合约变化""" # 读取组件数据 symbol = str(self.lineSymbol.text()) self.comboCurrency.setCurrentIndex(1) currency = safeUnicode(self.comboCurrency.currentText()) gatewayName = safeUnicode('quantos') # 查询合约 contract = self.mainEngine.getContract(symbol) # 清空价格数量 self.spinPrice.setValue(0) self.spinVolume.setValue(0) if contract: gatewayName = contract.gatewayName self.lineName.setText(contract.name) p = self.lineName.palette() p.setColor(self.lineName.foregroundRole(), QtGui.QColor('black')) self.lineName.setPalette(p) exchange = contract.exchange productClass = contract.productClass self.comboExchange.setCurrentIndex(self.exchangeList.index(exchange)) self.comboProductClass.setCurrentIndex(self.productClassList.index(productClass)) self.spinPrice.setSingleStep(contract.priceTick) self.spinVolume.setSingleStep(contract.lotsize) self.updateOffset() else: self.comboExchange.setCurrentIndex(0) self.comboProductClass.setCurrentIndex(0) productClass = safeUnicode(self.comboProductClass.currentText()) exchange = safeUnicode(self.comboExchange.currentText()) self.lineName.setText(u'不存在') p = self.lineName.palette() p.setColor(self.lineName.foregroundRole(), QtGui.QColor('red')) self.lineName.setPalette(p) # 清空行情显示 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('') if contract: # 重新注册事件监听 if self.signalemit != None: self.eventEngine.unregister(EVENT_TICK + self.symbol, self.signalemit) self.signalemit = self.signal.emit self.eventEngine.register(EVENT_TICK + symbol, self.signalemit) # 订阅合约 self.mainEngine.subscribe(contract.symbol, gatewayName) # 更新组件当前交易的合约 self.symbol = symbol # ---------------------------------------------------------------------- def format_price(self, price): return int(price * 1000) / 1000 # ---------------------------------------------------------------------- def updateTick(self, event): """更新行情""" tick = event.dict_['data'] if tick.symbol == self.symbol: contract = self.mainEngine.getContract(tick.symbol) price_tick = contract.priceTick self.labelBidPrice1.setText(str(self.format_price(tick.bidPrice1))) self.labelAskPrice1.setText(str(self.format_price(tick.askPrice1))) self.labelBidVolume1.setText(str(tick.bidVolume1)) self.labelAskVolume1.setText(str(tick.askVolume1)) if tick.bidPrice2: self.labelBidPrice2.setText(str(self.format_price(tick.bidPrice2))) self.labelBidPrice3.setText(str(self.format_price(tick.bidPrice3))) self.labelBidPrice4.setText(str(self.format_price(tick.bidPrice4))) self.labelBidPrice5.setText(str(self.format_price(tick.bidPrice5))) self.labelAskPrice2.setText(str(self.format_price(tick.askPrice2))) self.labelAskPrice3.setText(str(self.format_price(tick.askPrice3))) self.labelAskPrice4.setText(str(self.format_price(tick.askPrice4))) self.labelAskPrice5.setText(str(self.format_price(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(self.format_price(tick.lastPrice))) if self.spinPrice.value() < 0.000001 and tick.lastPrice > 0.000001: self.spinPrice.setValue(tick.lastPrice) if tick.preClosePrice: rt = (old_div(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()).strip() exchange = safeUnicode(self.comboExchange.currentText()) price = self.spinPrice.value() volume = self.spinVolume.value() gatewayName = safeUnicode('quantos') if len(symbol) <= 0 or len(exchange) <= 0 or price <= 0 or volume <= 0: return # 查询合约 contract = self.mainEngine.getContract(symbol) if contract: gatewayName = contract.gatewayName exchange = contract.exchange # 保证有交易所代码 req = VtOrderReq() idx = symbol.find(".") if idx != -1: req.symbol = symbol[0:idx] else: req.symbol = symbol req.exchange = exchange req.price = price req.volume = volume req.direction = safeUnicode(self.comboDirection.currentText()) req.priceType = safeUnicode(self.comboPriceType.currentText()) req.offset = safeUnicode(self.comboOffset.currentText()) req.urgency = self.spinUrgency.value() req.productClass = safeUnicode(self.comboProductClass.currentText()) 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.taskID 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.spinVolume.setValue(pos.enable) 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)) if self.comboProductClass.currentText() not in (PRODUCT_EQUITY, PRODUCT_BOND): self.tickOffset.setChecked(True) self.comboOffset.setCurrentIndex(self.offsetList.index(OFFSET_CLOSE) + 1) elif self.tickOffset.checkState(): self.comboOffset.setCurrentIndex(self.offsetList.index(OFFSET_CLOSE) + 1) # 价格留待更新后由用户输入,防止有误操作 def fillSymbol(self, cell): tick = cell.data self.lineSymbol.setText(tick.symbol) self.updateSymbol() if type(cell) in (BidCell, AskCell): price = str(cell.text()) if len(price) > 0: price = float(price) if price > 0: self.spinPrice.setValue(price) direction = DIRECTION_LONG if type(cell) is AskCell else DIRECTION_SHORT self.comboDirection.setCurrentIndex(self.directionList.index(direction)) self.updateOffset() ######################################################################## class ContractMonitor(BasicMonitor): """合约查询""" # ---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(ContractMonitor, self).__init__(parent=parent) self.mainEngine = mainEngine self.eventEngine = eventEngine d = OrderedDict() d['symbol'] = {'chinese': u'合约代码', 'cellType': BasicCell} d['exchange'] = {'chinese': u'交易所', 'cellType': BasicCell} d['name'] = {'chinese': u'名称', 'cellType': NameCell} d['productClass'] = {'chinese': u'合约类型', 'cellType': BasicCell} d['size'] = {'chinese': u'合约乘数', 'cellType': BasicCell} d['lotsize'] = {'chinese': u'最小交易单位', 'cellType': BasicCell} d['priceTick'] = {'chinese': u'最小价格变动', 'cellType': BasicCell} self.setHeaderDict(d) self.initUi() # ---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'合约') self.setLineWidth(1) self.setFont(BASIC_FONT) self.initTable() # 设置数据键 self.setDataKey('') self.setSorting(False) # 设置监控事件类型 self.setEventType(EVENT_CONTRACT, EVENT_CONTRACT_CLEAR) self.initTable() self.registerEvent()