class AlgoLogMonitor(QtWidgets.QTextEdit): """""" signal = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, algoEngine): """Constructor""" super(AlgoLogMonitor, self).__init__() self.eventEngine = algoEngine.eventEngine self.registerEvent() #---------------------------------------------------------------------- def registerEvent(self): """""" self.signal.connect(self.processEvent) self.eventEngine.register(EVENT_ALGO_LOG, self.signal.emit) #---------------------------------------------------------------------- def processEvent(self, event): """""" log = event.dict_['data'] if not log.gatewayName: log.gatewayName = u'算法引擎' msg = u'%s\t%s:%s' %(log.logTime, log.gatewayName, log.logContent) self.append(msg)
class StLogMonitor(QtWidgets.QTextEdit): """价差日志监控""" signal = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(StLogMonitor, self).__init__(parent) self.eventEngine = eventEngine self.registerEvent() #---------------------------------------------------------------------- def processLogEvent(self, event): """处理日志事件""" log = event.dict_['data'] content = '%s:%s' % (log.logTime, log.logContent) self.append(content) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signal.connect(self.processLogEvent) self.eventEngine.register(EVENT_SPREADTRADING_LOG, self.signal.emit)
class StActiveButton(QtWidgets.QPushButton): """""" signalActive = QtCore.Signal(bool) #---------------------------------------------------------------------- def __init__(self, algoEngine, spreadName, parent=None): """Constructor""" super(StActiveButton, self).__init__(parent) self.algoEngine = algoEngine self.spreadName = spreadName self.active = False self.setStopped() self.clicked.connect(self.buttonClicked) #---------------------------------------------------------------------- def buttonClicked(self): """改变运行模式""" if self.active: self.stop() else: self.start() #---------------------------------------------------------------------- def stop(self): """停止""" algoActive = self.algoEngine.stopAlgo(self.spreadName) if not algoActive: self.setStopped() #---------------------------------------------------------------------- def start(self): """启动""" algoActive = self.algoEngine.startAlgo(self.spreadName) if algoActive: self.setStarted() #---------------------------------------------------------------------- def setStarted(self): """算法启动""" self.setText(u'运行中') self.setStyleSheet(STYLESHEET_START) self.active = True self.signalActive.emit(self.active) #---------------------------------------------------------------------- def setStopped(self): """算法停止""" self.setText(u'已停止') self.setStyleSheet(STYLESHEET_STOP) self.active = False self.signalActive.emit(self.active)
class LogMonitor(BasicMonitor): """日志监控""" signalError = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(LogMonitor, self).__init__(mainEngine, eventEngine, parent) d = OrderedDict() d['gatewayName'] = {'chinese': vtText.GATEWAY, 'cellType': BasicCell} d['logTime'] = {'chinese': vtText.TIME, 'cellType': BasicCell} d['logContent'] = {'chinese': vtText.CONTENT, 'cellType': BasicCell} self.setHeaderDict(d) self.setEventType(EVENT_LOG) self.setFont(BASIC_FONT) self.initTable() self.registerEvent() self.signalError.connect(self.processErrorEvent) self.eventEngine.register(EVENT_ERROR, self.signalError.emit) # self.horizontalHeader().setResizeMode(2, QtWidgets.QHeaderView.Stretch) self.horizontalHeader().setSectionResizeMode( 2, QtWidgets.QHeaderView.Stretch) self.setFixedHeight(200) #---------------------------------------------------------------------- def processErrorEvent(self, event): """""" error = event.dict_['data'] logContent = u'发生错误,错误代码:%s,错误信息:%s' % (error.errorID, error.errorMsg) self.insertRow(0) cellLogTime = BasicCell(error.errorTime) cellLogContent = BasicCell(logContent) cellGatewayName = BasicCell(error.gatewayName) self.setItem(0, 0, cellGatewayName) self.setItem(0, 1, cellLogTime) self.setItem(0, 2, cellLogContent)
class RtdManager(QtWidgets.QWidget): """RTD管理工具""" signal = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, rsEngine, eventEngine, parent=None): """Constructor""" super(RtdManager, self).__init__(parent) self.rsEngine = rsEngine self.eventEngine = eventEngine self.initUi() self.registerEvent() #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signal.connect(self.updateLog) self.eventEngine.register(EVENT_RTDSERVICE_LOG, self.signal.emit) #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'RTD服务') self.logMonitor = QtWidgets.QTextBrowser() vbox = QtWidgets.QVBoxLayout() vbox.addWidget(self.logMonitor) self.setLayout(vbox) #---------------------------------------------------------------------- def updateLog(self, event): """输出日志""" log = event.dict_['data'] content = log.logTime + "\t" + log.logContent + '\n' self.logMonitor.append(content)
class JsEngineManager(QtWidgets.QWidget): """Jaqs服务管理组件""" signal = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, jsEngine, eventEngine, parent=None): """Constructor""" super(JsEngineManager, self).__init__(parent) self.jsEngine = jsEngine self.eventEngine = eventEngine self.initUi() self.registerEvent() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle('Jaqs服务') # 日志监控 self.logMonitor = QtWidgets.QTextEdit() self.logMonitor.setReadOnly(True) self.logMonitor.setMinimumHeight(600) # 设置布局 vbox = QtWidgets.QVBoxLayout() vbox.addWidget(self.logMonitor) self.setLayout(vbox) #---------------------------------------------------------------------- def updateLog(self, event): """更新日志""" log = event.dict_['data'] content = '\t'.join([log.logTime, log.logContent]) self.logMonitor.append(content) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signal.connect(self.updateLog) self.eventEngine.register(EVENT_JS_LOG, self.signal.emit)
class RqDataManager(QtWidgets.QWidget): """""" signal = QtCore.Signal(str) #---------------------------------------------------------------------- def __init__(self): """Constructor""" super(RqDataManager, self).__init__() self.client = None self.rq = None self.thread = Thread(target=self.run) self.productList = [] self.symbolExchangeDict = OrderedDict() self.initUi() n1 = self.connectMongo() if not n1: return n2 = self.initRqData() if not n2: return self.count = 0 self.active = True self.thread.start() #---------------------------------------------------------------------- def connectMongo(self): """连接数据库""" try: self.client = MongoClient(serverSelectionTimeoutMS=10) self.client.server_info() self.writeLog(u'MongoDB连接成功') return True except ConnectionFailure: self.client = None self.writeLog(u'MongoDB连接失败') return False #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'RQData数据服务') self.setWindowIcon(QtGui.QIcon('vnpy.ico')) self.setFixedHeight(500) self.setFixedWidth(900) self.logMonitor = QtWidgets.QTextEdit() self.logMonitor.setReadOnly(True) vbox = QtWidgets.QVBoxLayout() vbox.addWidget(self.logMonitor) self.setLayout(vbox) self.signal.connect(self.updateLog) # 托盘配置 self.tray = QtWidgets.QSystemTrayIcon() self.tray.setIcon(QtGui.QIcon('vnpy.ico')) self.tray.activated.connect(self.showManager) restoreAction = QtWidgets.QAction(u'还原', self, triggered=self.show) quitAction = QtWidgets.QAction(u'退出', self, triggered=self.exit) menu = QtWidgets.QMenu(QtWidgets.QApplication.desktop()) menu.addAction(restoreAction) menu.addAction(quitAction) self.tray.setContextMenu(menu) self.tray.show() #---------------------------------------------------------------------- def initRqData(self): """""" with open('config.json') as config: setting = json.load(config) for product in setting['product']: self.productList.append(product.upper()) # 检查是否填写了RQData配置 username = setting.get('rqUsername', None) password = setting.get('rqPassword', None) if not username or not password: self.writeLog(u'RQData的用户名和密码配置错误,请在config.json中修改') return False # 加载RQData try: import rqdatac as rq except ImportError: self.writeLog(u'没有安装RQData客户端,请先安装rqdatac') return False # 登录RQData self.rq = rq self.rq.init(username, password) # 获取本日可交易合约代码 try: df = self.rq.all_instruments(type='Future', date=datetime.now()) for ix, row in df.iterrows(): self.symbolExchangeDict[row['order_book_id']] = row['exchange'] except RuntimeError: self.writeLog(u'RQData的用户名和密码无效,请联系米筐申请试用或者购买') return False self.writeLog(u'RQData客户端登录成功') return True #---------------------------------------------------------------------- def downloadBar(self, symbol, frequency): """下载合约数据""" if 'frequency' == '1m': db = self.client[MINUTE_DB_NAME] else: db = self.client[DAILY_DB_NAME] # 上期所和大商所代码改为小写 exchange = self.symbolExchangeDict[symbol] if exchange in ['SHFE', 'DCE']: localSymbol = symbol.lower() else: localSymbol = symbol collection = db[localSymbol] # 获取本地数据库中最后一条记录的时间,并下载新数据 result = collection.find_one(sort=[("datetime", DESCENDING)]) if result: startDate = result['datetime'] else: startDate = '20180101' if startDate: self.writeLog(u'%s下载更新数据,开始时间:%s' % (localSymbol, startDate)) else: self.writeLog(u'%s初次下载数据,耗时可能较长,请耐心等待' % (localSymbol)) df = self.rq.get_price( symbol, frequency=frequency, fields=['open', 'high', 'low', 'close', 'volume'], start_date=startDate, end_date=datetime.now()) # 插入到数据库 for ix, row in df.iterrows(): bar = self.generateBar(row, localSymbol) d = bar.__dict__ flt = {'datetime': bar.datetime} collection.replace_one(flt, d, True) self.writeLog(u'%s数据更新完成:%s - %s' % (localSymbol, df.index[0], df.index[-1])) #---------------------------------------------------------------------- def generateBar(self, row, symbol): """生成K线对象""" bar = VtBarData() bar.symbol = symbol bar.vtSymbol = symbol bar.open = row['open'] bar.high = row['high'] bar.low = row['low'] bar.close = row['close'] bar.volume = row['volume'] bar.datetime = row.name bar.date = bar.datetime.strftime("%Y%m%d") bar.time = bar.datetime.strftime("%H:%M:%S") return bar #---------------------------------------------------------------------- def writeLog(self, msg): """记录日志""" self.signal.emit(msg) #---------------------------------------------------------------------- def updateLog(self, msg): """更新日志""" dt = datetime.now() msg = '%s: %s' % (dt, msg) self.logMonitor.append(msg) #---------------------------------------------------------------------- def run(self): """运行""" while self.active: sleep(1) self.count += 1 if self.count < 10: continue self.count = 0 now = datetime.now().time() if ((DAY_START <= now <= DAY_END) or (now >= NIGHT_START) or (now <= NIGHT_END)): for symbol in self.symbolExchangeDict.keys(): download = False for product in self.productList: if product in symbol: download = True if download: self.downloadBar(symbol, '1m') else: self.writeLog(u'非交易时间段,不执行更新') #---------------------------------------------------------------------- def showManager(self, reason): """""" self.show() #---------------------------------------------------------------------- def closeEvent(self, event): """""" self.hide() event.ignore() #---------------------------------------------------------------------- def exit(self): """""" self.active = False self.thread.join() QtWidgets.qApp.quit()
class AlgoStatusMonitor(QtWidgets.QTableWidget): """算法状态监控""" signalParam = QtCore.Signal(type(Event())) signalVar = QtCore.Signal(type(Event())) MODE_WORKING = 'working' MODE_HISTORY = 'history' #---------------------------------------------------------------------- def __init__(self, algoEngine, mode): """Constructor""" super(AlgoStatusMonitor, self).__init__() self.algoEngine = algoEngine self.eventEngine = algoEngine.eventEngine self.mode = mode self.cellDict = {} self.initUi() self.registerEvent() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" labels = [u'', u'名称', u'参数', u'变量'] self.setColumnCount(len(labels)) self.setHorizontalHeaderLabels(labels) self.setRowCount(0) self.verticalHeader().setVisible(False) self.setEditTriggers(self.NoEditTriggers) self.setAlternatingRowColors(True) if self.mode == self.MODE_HISTORY: self.hideColumn(0) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signalParam.connect(self.processParamEvent) self.signalVar.connect(self.processVarEvent) self.eventEngine.register(EVENT_ALGO_PARAM, self.signalParam.emit) self.eventEngine.register(EVENT_ALGO_VAR, self.signalVar.emit) #---------------------------------------------------------------------- def addAlgo(self, algoName): """新增算法""" self.insertRow(0) buttonStop = StopButton(self.algoEngine, algoName) cellName = AlgoCell(algoName) cellParam = AlgoCell() cellVar = AlgoCell() self.setCellWidget(0, 0, buttonStop) self.setItem(0, 1, cellName) self.setItem(0, 2, cellParam) self.setItem(0, 3, cellVar) self.cellDict[algoName] = { 'param': cellParam, 'var': cellVar, 'button': buttonStop } if self.mode == self.MODE_HISTORY: self.hideRow(0) #---------------------------------------------------------------------- def processParamEvent(self, event): """处理参数事件""" d = event.dict_['data'] algoName = d['algoName'] if algoName not in self.cellDict: self.addAlgo(algoName) text = self.generateText(d) cell = self.cellDict[algoName]['param'] cell.setText(text) self.resizeColumnsToContents() #---------------------------------------------------------------------- def processVarEvent(self, event): """处理变量事件""" d = event.dict_['data'] algoName = d['algoName'] if algoName not in self.cellDict: self.addAlgo(algoName) if 'active' in d: active = d['active'] # 若算法已经结束 if not active: # 禁用按钮 cells = self.cellDict[algoName] button = cells['button'] button.disable() # 根据模式决定显示或者隐藏该行 cell = cells['var'] row = self.row(cell) if self.mode == self.MODE_WORKING: self.hideRow(row) else: self.showRow(row) text = self.generateText(d) cell = self.cellDict[algoName]['var'] cell.setText(text) self.resizeColumnsToContents() #---------------------------------------------------------------------- def generateText(self, d): """从字典生成字符串""" l = [] for k, v in d.items(): if k not in ['algoName']: msg = u'%s:%s' %(k, v) l.append(msg) text = ','.join(l) return text
class AlgoSettingMonitor(QtWidgets.QTableWidget): """""" signal = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, algoEngine): """Constructor""" super(AlgoSettingMonitor, self).__init__() self.algoEngine = algoEngine self.eventEngine = algoEngine.eventEngine self.cellDict = {} self.initUi() self.registerEvent() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" labels = ['', '', u'名称', u'算法', u'参数'] self.setColumnCount(len(labels)) self.setHorizontalHeaderLabels(labels) self.setRowCount(0) self.verticalHeader().setVisible(False) self.setEditTriggers(self.NoEditTriggers) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signal.connect(self.processEvent) self.eventEngine.register(EVENT_ALGO_SETTING, self.signal.emit) #---------------------------------------------------------------------- def processEvent(self, event): """处理事件""" setting = event.dict_['data'] settingName = setting['settingName'] # 删除配置行 if len(setting) == 1: d = self.cellDict.pop(settingName) cell = d['text'] row = self.row(cell) self.removeRow(row) # 新增配置行 elif settingName not in self.cellDict: self.insertRow(0) buttonStart = StartButton(self.algoEngine, setting) buttonDelete = DeleteButton(self.algoEngine, setting) cellSettingName = AlgoCell(settingName) cellTemplateName = AlgoCell(setting['templateName']) cellSettingText = AlgoCell(self.generateText(setting)) self.setCellWidget(0, 0, buttonStart) self.setCellWidget(0, 1, buttonDelete) self.setItem(0, 2, cellSettingName) self.setItem(0, 3, cellTemplateName) self.setItem(0, 4, cellSettingText) self.cellDict[settingName] = { 'start': buttonStart, 'template': cellTemplateName, 'text': cellSettingText, 'delete': buttonDelete } # 更新已有配置行 else: d = self.cellDict[settingName] d['start'].updateSetting(setting) d['template'].setText(setting['templateName']) d['text'].setText(self.generateText(setting)) d['delete'].updateSetting(setting) self.resizeColumnsToContents() #---------------------------------------------------------------------- def generateText(self, d): """从字典生成字符串""" l = [] for k, v in d.items(): if k not in ['settingName', 'templateName', '_id']: msg = u'%s:%s' %(k, v) l.append(msg) text = ','.join(l) return text
class CustomMenu(QtWidgets.QPushButton): """合约管理组件""" signal = QtCore.Signal(type(Event())) # ---------------------------------------------------------------------- def __init__(self, parent): """Constructor""" super(CustomMenu, self).__init__() self.parent = parent # self.initUi() self.initMenu() #----------------------------------------------------------------------- def initMenu(self): self.setStyleSheet( "QMenu{background:purple;}" "QMenu{border:1px solid lightgray;}" "QMenu{border-color:green;}" "QMenu::item{padding:0px 20px 0px 15px;}" "QMenu::item{height:30px;}" "QMenu::item{color:blue;}" "QMenu::item{background:white;}" "QMenu::item{margin:1px 0px 0px 0px;}" "QMenu::item:selected:enabled{background:lightgray;}" "QMenu::item:selected:enabled{color:blue;}" "QMenu::item:selected:!enabled{background:transparent;}" "QMenu::separator{height:50px;}" "QMenu::separator{width:1px;}" "QMenu::separator{background:white;}" "QMenu::separator{margin:1px 1px 1px 1px;}" "QMenu#menu{background:white;}" "QMenu#menu{border:1px solid lightgray;}" "QMenu#menu::item{padding:0px 20px 0px 15px;}" "QMenu#menu::item{height:15px;}" "QMenu#menu::item:selected:enabled{background:lightgray;}" "QMenu#menu::item:selected:enabled{color:white;}" "QMenu#menu::item:selected:!enabled{background:transparent;}" "QMenu#menu::separator{height:1px;}" "QMenu#menu::separator{background:lightgray;}" "QMenu#menu::separator{margin:2px 0px 2px 0px;}" "QMenu#menu::indicator {padding:5px;}") self.color = QColor(Qt.gray) self.opacity = 1.0 '''''' ' 创建右键菜单 ' '' # 必须将ContextMenuPolicy设置为Qt.CustomContextMenu # 否则无法使用customContextMenuRequested信号 self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.showContextMenu) # 创建QMenu self.contextMenu = QMenu(self) self.trendMenu = self.contextMenu.addMenu(u"k线形态") self.swingMenu = self.contextMenu.addMenu(u"技术指标") self.amountMenu = self.contextMenu.addMenu(u"策略研究") self.pzxzMenu = self.contextMenu.addMenu(u"品种选择") # 添加二级菜单 #趋势分析指标 self.actionSAR = self.trendMenu.addAction(u'k线') self.actionSAR.triggered.connect( lambda: self.parent.initIndicator(u"KLINE")) self.actionMA = self.trendMenu.addAction(u'信号隐藏') self.actionMA.triggered.connect( lambda: self.parent.initIndicator(u"信号隐藏")) self.actionMA = self.trendMenu.addAction(u'信号显示') self.actionMA.triggered.connect( lambda: self.parent.initIndicator(u"信号显示")) #摆动分析 self.actionCCI = self.swingMenu.addAction(u'MA SHORT') self.actionCCI.triggered.connect( lambda: self.parent.initIndicator(u"MA SHORT")) self.actionROC = self.swingMenu.addAction(u'MA LONG') self.actionROC.triggered.connect( lambda: self.parent.initIndicator(u"MA LONG")) self.actionSHORTTERM = self.swingMenu.addAction(u'SHORT TERM(Limit)') self.actionSHORTTERM.triggered.connect( lambda: self.parent.initIndicator(u"SHORT TERM(Limit)")) self.actionSHORTTERMF = self.swingMenu.addAction(u'SHORT TERM(First)') self.actionSHORTTERMF.triggered.connect( lambda: self.parent.initIndicator(u"SHORT TERM(First)")) self.actionSHORTTERMALL = self.swingMenu.addAction(u'SHORT TERM(All)') self.actionSHORTTERMALL.triggered.connect( lambda: self.parent.initIndicator(u"SHORT TERM(All)")) self.actionWAIBAORI = self.swingMenu.addAction(u'外包日') self.actionWAIBAORI.triggered.connect( lambda: self.parent.initIndicator(u"外包日")) self.actionGJR_BUY = self.swingMenu.addAction(u'攻击日(买入)') self.actionGJR_BUY.triggered.connect( lambda: self.parent.initIndicator(u"攻击日(买入)")) self.actionGJR_SELL = self.swingMenu.addAction(u'攻击日(卖出)') self.actionGJR_SELL.triggered.connect( lambda: self.parent.initIndicator(u"攻击日(卖出)")) ##设为起始日期 self.actionOPI = self.amountMenu.addAction(u'设为起始日期') self.actionOPI.triggered.connect( lambda: self.parent.initIndicator(u"设为起始日期")) self.actionOPI1 = self.amountMenu.addAction(u'设为结束日期') self.actionOPI1.triggered.connect( lambda: self.parent.initIndicator(u"设为结束日期")) ##量仓分析 self.actionOPI2 = self.amountMenu.addAction(u'MA_螺纹空_PLUS') self.actionOPI2.triggered.connect( lambda: self.parent.initIndicator(u"MA_螺纹空_PLUS")) ##成交量分析 self.actionVOL = self.amountMenu.addAction(u'SHORTTERM_螺纹_多') self.actionVOL.triggered.connect( lambda: self.parent.initIndicator(u"SHORTTERM_螺纹_多")) self.action1 = self.amountMenu.addAction(u'SHORTTERM_螺纹_空') self.action1.triggered.connect( lambda: self.parent.initIndicator(u"SHORTTERM_螺纹_空")) self.action2 = self.amountMenu.addAction(u'SHORTTERM_螺纹_多_加仓') self.action2.triggered.connect( lambda: self.parent.initIndicator(u"SHORTTERM_螺纹_多_加仓")) self.action5 = self.amountMenu.addAction(u'VOLATILITY_螺纹_多') self.action5.triggered.connect( lambda: self.parent.initIndicator(u"VOLATILITY_螺纹_多")) self.action6 = self.amountMenu.addAction(u'VOLATILITY_螺纹_空') self.action6.triggered.connect( lambda: self.parent.initIndicator(u"VOLATILITY_螺纹_空")) self.action9 = self.amountMenu.addAction(u'VOLATILITY_螺纹_V1') self.action9.triggered.connect( lambda: self.parent.initIndicator(u"VOLATILITY_螺纹_V1")) self.action7 = self.amountMenu.addAction(u'外包日_螺纹_多') self.action7.triggered.connect( lambda: self.parent.initIndicator(u"外包日_螺纹_多")) self.action3 = self.pzxzMenu.addAction(u'RB9999') self.action3.triggered.connect( lambda: self.parent.initIndicator(u"RB9999")) self.action4 = self.pzxzMenu.addAction(u'BU9999') self.action4.triggered.connect( lambda: self.parent.initIndicator(u"BU9999")) #self.contextMenu.exec_(QCursor.pos()) # 在鼠标位置显示 #添加二级菜单 def showContextMenu(self, pos): ''''' 右键点击时调用的函数 ''' # 菜单显示前,将它移动到鼠标点击的位置 # self.contextMenu.move(self.pos() + pos) self.contextMenu.show() self.contextMenu.exec_(QCursor.pos())
class TcManager(QtWidgets.QWidget): """""" REQ_ADDRESS = 'tcp://localhost:2015' SUB_ADDRESS = 'tcp://localhost:2018' REP_ADDRESS = 'tcp://*:2015' PUB_ADDRESS = 'tcp://*:2018' COPY_RATIO = '1' INTERVAL = '1' settingFileName = 'TradeCopy.vt' settingFilePath = getTempPath(settingFileName) signal = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, tcEngine, eventEngine, parent=None): """Constructor""" super(TcManager, self).__init__(parent) self.tcEngine = tcEngine self.eventEngine = eventEngine self.initUi() self.loadSetting() self.registerEvent() self.tcEngine.writeLog(u'欢迎使用TradeCopy交易复制模块') #---------------------------------------------------------------------- def initUi(self): """""" self.setWindowTitle(u'交易复制') self.setMinimumWidth(700) self.setMinimumHeight(700) # 创建组件 self.lineReqAddress = QtWidgets.QLineEdit(self.REQ_ADDRESS) self.lineSubAddress= QtWidgets.QLineEdit(self.SUB_ADDRESS) self.lineRepAddress = QtWidgets.QLineEdit(self.REP_ADDRESS) self.linePubAddress = QtWidgets.QLineEdit(self.PUB_ADDRESS) validator = QtGui.QDoubleValidator() validator.setBottom(0) self.lineCopyRatio = QtWidgets.QLineEdit() self.lineCopyRatio.setValidator(validator) self.lineCopyRatio.setText(self.COPY_RATIO) validator2 = QtGui.QIntValidator() validator2.setBottom(1) self.lineInterval = QtWidgets.QLineEdit() self.lineInterval.setValidator(validator2) self.lineInterval.setText(self.INTERVAL) self.buttonProvider = QtWidgets.QPushButton(u'启动发布者') self.buttonProvider.clicked.connect(self.startProvider) self.buttonSubscriber = QtWidgets.QPushButton(u'启动订阅者') self.buttonSubscriber.clicked.connect(self.startSubscriber) self.buttonStopEngine = QtWidgets.QPushButton(u'停止') self.buttonStopEngine.clicked.connect(self.stopEngine) self.buttonStopEngine.setEnabled(False) self.buttonResetAddress = QtWidgets.QPushButton(u'重置地址') self.buttonResetAddress.clicked.connect(self.resetAddress) self.logMonitor = QtWidgets.QTextEdit() self.logMonitor.setReadOnly(True) self.widgetList = [ self.lineCopyRatio, self.lineInterval, self.linePubAddress, self.lineSubAddress, self.lineRepAddress, self.lineReqAddress, self.buttonProvider, self.buttonSubscriber, self.buttonResetAddress ] # 布局 QLabel = QtWidgets.QLabel grid = QtWidgets.QGridLayout() grid.addWidget(QLabel(u'响应地址'), 0, 0) grid.addWidget(self.lineRepAddress, 0, 1) grid.addWidget(QLabel(u'请求地址'), 0, 2) grid.addWidget(self.lineReqAddress, 0, 3) grid.addWidget(QLabel(u'发布地址'), 1, 0) grid.addWidget(self.linePubAddress, 1, 1) grid.addWidget(QLabel(u'订阅地址'), 1, 2) grid.addWidget(self.lineSubAddress, 1, 3) grid.addWidget(QLabel(u'发布间隔(秒)'), 2, 0) grid.addWidget(self.lineInterval, 2, 1) grid.addWidget(QLabel(u'复制比例(倍)'), 2, 2) grid.addWidget(self.lineCopyRatio, 2, 3) grid.addWidget(self.buttonProvider, 3, 0, 1, 2) grid.addWidget(self.buttonSubscriber, 3, 2, 1, 2) grid.addWidget(self.buttonStopEngine, 4, 0, 1, 2) grid.addWidget(self.buttonResetAddress, 4, 2, 1, 2) grid.addWidget(self.logMonitor, 5, 0, 1, 4) self.setLayout(grid) #---------------------------------------------------------------------- def saveSetting(self): """""" f = shelve.open(self.settingFilePath) f['repAddress'] = self.lineRepAddress.text() f['reqAddress'] = self.lineReqAddress.text() f['pubAddress'] = self.linePubAddress.text() f['subAddress'] = self.lineSubAddress.text() f['copyRatio'] = self.lineCopyRatio.text() f['interval'] = self.lineInterval.text() f.close() #---------------------------------------------------------------------- def loadSetting(self): """""" f = shelve.open(self.settingFilePath) if f: self.lineRepAddress.setText(f['repAddress']) self.lineReqAddress.setText(f['reqAddress']) self.linePubAddress.setText(f['pubAddress']) self.lineSubAddress.setText(f['subAddress']) self.lineCopyRatio.setText(f['copyRatio']) self.lineInterval.setText(f['interval']) f.close() #---------------------------------------------------------------------- def resetAddress(self): """""" self.lineReqAddress.setText(self.REQ_ADDRESS) self.lineRepAddress.setText(self.REP_ADDRESS) self.linePubAddress.setText(self.PUB_ADDRESS) self.lineSubAddress.setText(self.SUB_ADDRESS) #---------------------------------------------------------------------- def stopEngine(self): """""" self.tcEngine.stop() for widget in self.widgetList: widget.setEnabled(True) self.buttonStopEngine.setEnabled(False) #---------------------------------------------------------------------- def registerEvent(self): """""" self.signal.connect(self.processLogEvent) self.eventEngine.register(EVENT_TC_LOG, self.signal.emit) #---------------------------------------------------------------------- def processLogEvent(self, event): """""" log = event.dict_['data'] txt = '%s: %s' %(log.logTime, log.logContent) self.logMonitor.append(txt) #---------------------------------------------------------------------- def startProvider(self): """""" repAddress = str(self.lineRepAddress.text()) pubAddress = str(self.linePubAddress.text()) interval = int(self.lineInterval.text()) self.tcEngine.startProvider(repAddress, pubAddress, interval) for widget in self.widgetList: widget.setEnabled(False) self.buttonStopEngine.setEnabled(True) #---------------------------------------------------------------------- def startSubscriber(self): """""" reqAddress = str(self.lineReqAddress.text()) subAddress = str(self.lineSubAddress.text()) copyRatio = float(self.lineCopyRatio.text()) self.tcEngine.startSubscriber(reqAddress, subAddress, copyRatio) for widget in self.widgetList: widget.setEnabled(False) self.buttonStopEngine.setEnabled(True)
class TradingWidget(QtWidgets.QFrame): """简单交易组件""" signal = QtCore.Signal(type(Event())) directionList = [DIRECTION_LONG, DIRECTION_SHORT] offsetList = [OFFSET_OPEN, OFFSET_CLOSE, OFFSET_NONE] priceTypeList = [PRICETYPE_LIMITPRICE, PRICETYPE_MARKETPRICE] gatewayList = [''] #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(TradingWidget, self).__init__(parent) self.mainEngine = mainEngine self.eventEngine = eventEngine self.vtSymbol = '' # 添加交易接口 l = mainEngine.getAllGatewayDetails() gatewayNameList = [d['gatewayName'] for d in l] self.gatewayList.extend(gatewayNameList) self.initUi() self.registerEvent() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(vtText.TRADING) self.setFixedHeight(400) self.setFixedWidth(600) self.setFrameShape(self.Box) # 设置边框 self.setLineWidth(1) # 左边部分 labelPriceType = QtWidgets.QLabel(vtText.PRICE_TYPE) labelSymbol = QtWidgets.QLabel(u'VT代码') labelPrice = QtWidgets.QLabel(vtText.PRICE) labelVolume = QtWidgets.QLabel(u'数量') labelOffset = QtWidgets.QLabel(vtText.OFFSET) self.comboPriceType = QtWidgets.QComboBox() self.comboPriceType.addItems(self.priceTypeList) self.lineSymbol = QtWidgets.QLineEdit() validator = QtGui.QDoubleValidator() validator.setBottom(0) self.linePrice = QtWidgets.QLineEdit() self.linePrice.setValidator(validator) self.lineVolume = QtWidgets.QLineEdit() self.lineVolume.setValidator(validator) self.comboOffset = QtWidgets.QComboBox() self.comboOffset.addItems(self.offsetList) gridLeft = QtWidgets.QGridLayout() gridLeft.addWidget(labelPriceType, 0, 0) gridLeft.addWidget(labelSymbol, 1, 0) gridLeft.addWidget(labelPrice, 2, 0) gridLeft.addWidget(labelVolume, 3, 0) gridLeft.addWidget(labelOffset, 4, 0) gridLeft.addWidget(self.comboPriceType, 0, 1) gridLeft.addWidget(self.lineSymbol, 1, 1) gridLeft.addWidget(self.linePrice, 2, 1) gridLeft.addWidget(self.lineVolume, 3, 1) gridLeft.addWidget(self.comboOffset, 4, 1) # 右边部分 self.depthMonitor = DepthMonitor(self.mainEngine, self.eventEngine) # 发单按钮 buttonBuy = QtWidgets.QPushButton(u'买/多') buttonSell = QtWidgets.QPushButton(u'卖/空') buttonCancelAll = QtWidgets.QPushButton(vtText.CANCEL_ALL) size = buttonBuy.sizeHint() buttonBuy.setMinimumHeight(size.height()*2) buttonSell.setMinimumHeight(size.height()*2) buttonCancelAll.setMinimumHeight(size.height()*2) buttonBuy.clicked.connect(self.sendBuyOrder) buttonSell.clicked.connect(self.sendSellOrder) buttonCancelAll.clicked.connect(self.cancelAll) buttonBuy.setStyleSheet('color:white;background-color:red') buttonSell.setStyleSheet('color:white;background-color:green') buttonCancelAll.setStyleSheet('color:black;background-color:yellow') gridButton = QtWidgets.QGridLayout() gridButton.addWidget(buttonBuy, 0, 0) gridButton.addWidget(buttonSell, 0, 1) gridButton.addWidget(buttonCancelAll, 1, 0, 1, 2) # 整合布局 vbox = QtWidgets.QVBoxLayout() vbox.addLayout(gridLeft) vbox.addLayout(gridButton) hbox = QtWidgets.QHBoxLayout() hbox.addLayout(vbox) hbox.addWidget(self.depthMonitor) self.setLayout(hbox) # 关联更新 self.lineSymbol.returnPressed.connect(self.updateSymbol) self.depthMonitor.itemDoubleClicked.connect(self.updatePrice) #---------------------------------------------------------------------- def updateSymbol(self): """合约变化""" self.vtSymbol = str(self.lineSymbol.text()) contract = self.mainEngine.getContract(self.vtSymbol) if not contract: return # 清空价格数量 self.linePrice.clear() self.lineVolume.clear() self.depthMonitor.updateVtSymbol(self.vtSymbol) # 订阅合约 req = VtSubscribeReq() req.symbol = contract.symbol self.mainEngine.subscribe(req, contract.gatewayName) #---------------------------------------------------------------------- def updateTick(self, event): """更新行情""" tick = event.dict_['data'] if tick.vtSymbol != self.vtSymbol: return self.depthMonitor.updateTick(tick) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signal.connect(self.updateTick) self.eventEngine.register(EVENT_TICK, self.signal.emit) #---------------------------------------------------------------------- def updatePrice(self, cell): """""" try: price = cell.price except AttributeError: return self.linePrice.setText(str(price)) #---------------------------------------------------------------------- def sendOrder(self, direction): """发单""" vtSymbol = str(self.lineSymbol.text()) contract = self.mainEngine.getContract(vtSymbol) if not contract: return # 获取价格 priceText = self.linePrice.text() if not priceText: return price = float(priceText) # 获取数量 volumeText = self.lineVolume.text() if not volumeText: return if '.' in volumeText: volume = float(volumeText) else: volume = int(volumeText) # 委托 req = VtOrderReq() req.symbol = contract.symbol req.vtSymbol = ':'.join([contract.symbol,contract.gatewayName]) req.price = price req.volume = volume req.direction = direction req.priceType = text_type(self.comboPriceType.currentText()) req.offset = text_type(self.comboOffset.currentText()) req.byStrategy = 'Manual' self.mainEngine.sendOrder(req, contract.gatewayName) #---------------------------------------------------------------------- def sendBuyOrder(self): """""" self.sendOrder(DIRECTION_LONG) #---------------------------------------------------------------------- def sendSellOrder(self): """""" self.sendOrder(DIRECTION_SHORT) #---------------------------------------------------------------------- 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)
class WeixinEngineManager(QtWidgets.QWidget): """行情数据记录引擎管理组件""" signal = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, drEngine, eventEngine, parent=None): """Constructor""" super(DrEngineManager, self).__init__(parent) self.drEngine = drEngine self.eventEngine = eventEngine self.initUi() self.updateSetting() self.registerEvent() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(text.DATA_RECORDER) # 记录合约配置监控 tickLabel = QtWidgets.QLabel(text.TICK_RECORD) self.tickTable = QtWidgets.QTableWidget() self.tickTable.setColumnCount(2) self.tickTable.verticalHeader().setVisible(False) self.tickTable.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers) self.tickTable.setAlternatingRowColors(True) self.tickTable.setHorizontalHeaderLabels( [text.CONTRACT_SYMBOL, text.GATEWAY]) barLabel = QtWidgets.QLabel(text.BAR_RECORD) self.barTable = QtWidgets.QTableWidget() self.barTable.setColumnCount(2) self.barTable.verticalHeader().setVisible(False) self.barTable.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers) self.barTable.setAlternatingRowColors(True) self.barTable.setHorizontalHeaderLabels( [text.CONTRACT_SYMBOL, text.GATEWAY]) activeLabel = QtWidgets.QLabel(text.DOMINANT_CONTRACT) self.activeTable = QtWidgets.QTableWidget() self.activeTable.setColumnCount(2) self.activeTable.verticalHeader().setVisible(False) self.activeTable.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers) self.activeTable.setAlternatingRowColors(True) self.activeTable.setHorizontalHeaderLabels( [text.DOMINANT_SYMBOL, text.CONTRACT_SYMBOL]) # 日志监控 self.logMonitor = QtWidgets.QTextEdit() self.logMonitor.setReadOnly(True) self.logMonitor.setMinimumHeight(600) # 设置布局 grid = QtWidgets.QGridLayout() grid.addWidget(tickLabel, 0, 0) grid.addWidget(barLabel, 0, 1) grid.addWidget(activeLabel, 0, 2) grid.addWidget(self.tickTable, 1, 0) grid.addWidget(self.barTable, 1, 1) grid.addWidget(self.activeTable, 1, 2) vbox = QtWidgets.QVBoxLayout() vbox.addLayout(grid) vbox.addWidget(self.logMonitor) self.setLayout(vbox) #---------------------------------------------------------------------- def updateLog(self, event): """更新日志""" log = event.dict_['data'] content = '\t'.join([log.logTime, log.logContent]) self.logMonitor.append(content) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signal.connect(self.updateLog) self.eventEngine.register(EVENT_ORDER, self.signal.emit) #---------------------------------------------------------------------- def updateSetting(self): """显示引擎行情记录配置""" setting, activeSetting = self.drEngine.getSetting() for d in setting.values(): if 'tick' in d and d['tick']: self.tickTable.insertRow(0) self.tickTable.setItem(0, 0, TableCell(d['symbol'])) self.tickTable.setItem(0, 1, TableCell(d['gateway'])) if 'bar' in d and d['bar']: self.barTable.insertRow(0) self.barTable.setItem(0, 0, TableCell(d['symbol'])) self.barTable.setItem(0, 1, TableCell(d['gateway'])) for vtSymbol, activeSymbol in activeSetting.items(): self.activeTable.insertRow(0) self.activeTable.setItem(0, 0, TableCell(activeSymbol)) self.activeTable.setItem(0, 1, TableCell(vtSymbol)) self.tickTable.resizeColumnsToContents() self.barTable.resizeColumnsToContents() self.activeTable.resizeColumnsToContents()
class CustomMenu(QtWidgets.QPushButton): """合约管理组件""" signal = QtCore.Signal(type(Event())) # ---------------------------------------------------------------------- def __init__(self, parent): """Constructor""" super(CustomMenu, self).__init__() self.parent = parent # self.initUi() self.initMenu() #----------------------------------------------------------------------- def initMenu(self): self.setStyleSheet( "QMenu{background:purple;}" "QMenu{border:1px solid lightgray;}" "QMenu{border-color:green;}" "QMenu::item{padding:0px 20px 0px 15px;}" "QMenu::item{height:30px;}" "QMenu::item{color:blue;}" "QMenu::item{background:white;}" "QMenu::item{margin:1px 0px 0px 0px;}" "QMenu::item:selected:enabled{background:lightgray;}" "QMenu::item:selected:enabled{color:blue;}" "QMenu::item:selected:!enabled{background:transparent;}" "QMenu::separator{height:50px;}" "QMenu::separator{width:1px;}" "QMenu::separator{background:white;}" "QMenu::separator{margin:1px 1px 1px 1px;}" "QMenu#menu{background:white;}" "QMenu#menu{border:1px solid lightgray;}" "QMenu#menu::item{padding:0px 20px 0px 15px;}" "QMenu#menu::item{height:15px;}" "QMenu#menu::item:selected:enabled{background:lightgray;}" "QMenu#menu::item:selected:enabled{color:white;}" "QMenu#menu::item:selected:!enabled{background:transparent;}" "QMenu#menu::separator{height:1px;}" "QMenu#menu::separator{background:lightgray;}" "QMenu#menu::separator{margin:2px 0px 2px 0px;}" "QMenu#menu::indicator {padding:5px;}") self.color = QColor(Qt.gray) self.opacity = 1.0 '''''' ' 创建右键菜单 ' '' # 必须将ContextMenuPolicy设置为Qt.CustomContextMenu # 否则无法使用customContextMenuRequested信号 self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.showContextMenu) # 创建QMenu self.contextMenu = QMenu(self) self.trendMenu = self.contextMenu.addMenu(u"k线形态") self.swingMenu = self.contextMenu.addMenu(u"技术指标") self.amountMenu = self.contextMenu.addMenu(u"策略研究") # 添加二级菜单 #趋势分析指标 self.actionSAR = self.trendMenu.addAction(u'k线') self.actionSAR.triggered.connect( lambda: self.parent.initIndicator(u"KLINE")) self.actionMA = self.trendMenu.addAction(u'信号隐藏') self.actionMA.triggered.connect( lambda: self.parent.initIndicator(u"信号隐藏")) self.actionMA = self.trendMenu.addAction(u'信号显示') self.actionMA.triggered.connect( lambda: self.parent.initIndicator(u"信号显示")) #摆动分析 self.actionCCI = self.swingMenu.addAction(u'MA SHORT') self.actionCCI.triggered.connect( lambda: self.parent.initIndicator(u"MA SHORT")) self.actionROC = self.swingMenu.addAction(u'MA LONG') self.actionROC.triggered.connect( lambda: self.parent.initIndicator(u"MA LONG")) ##设为起始日期 self.actionOPI = self.amountMenu.addAction(u'设为起始日期') self.actionOPI.triggered.connect( lambda: self.parent.initIndicator(u"设为起始日期")) ##量仓分析 self.actionOPI = self.amountMenu.addAction(u'MA_螺纹多_PLUS') self.actionOPI.triggered.connect( lambda: self.parent.initIndicator(u"MA_螺纹多_PLUS")) ##成交量分析 self.actionVOL = self.amountMenu.addAction(u'CJL') self.actionVOL.triggered.connect( lambda: self.parent.initIndicator(u"CJL")) #self.contextMenu.exec_(QCursor.pos()) # 在鼠标位置显示 #添加二级菜单 def showContextMenu(self, pos): ''''' 右键点击时调用的函数 ''' # 菜单显示前,将它移动到鼠标点击的位置 # self.contextMenu.move(self.pos() + pos) self.contextMenu.show() self.contextMenu.exec_(QCursor.pos())
class BasicMonitor(QtWidgets.QTableWidget): """ 基础监控 headerDict中的值对应的字典格式如下 {'chinese': u'中文名', 'cellType': BasicCell} """ signal = QtCore.Signal(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.columnResized = False # 字体 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) # 调整列宽 if not self.columnResized: self.resizeColumns() self.columnResized = True # 重新打开排序 if self.sorting: self.setSortingEnabled(True) #---------------------------------------------------------------------- def resizeColumns(self): """调整各列的大小""" self.horizontalHeader().resizeSections( QtWidgets.QHeaderView.ResizeToContents) #---------------------------------------------------------------------- def setSorting(self, sorting): """设置是否允许根据表头排序""" self.sorting = sorting #---------------------------------------------------------------------- def saveToCsv(self): """保存表格内容到CSV文件""" # 先隐藏右键菜单 self.menu.close() # 获取想要保存的文件名 path = QtWidgets.QFileDialog.getSaveFileName(self, vtText.SAVE_DATA, '', 'CSV(*.csv)') try: #if not path.isEmpty(): if path: 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 = QtWidgets.QMenu(self) saveAction = QtWidgets.QAction(vtText.SAVE_DATA, self) saveAction.triggered.connect(self.saveToCsv) self.menu.addAction(saveAction) #---------------------------------------------------------------------- def contextMenuEvent(self, event): """右键点击事件""" self.menu.popup(QtGui.QCursor.pos())
class TradingWidget(QtWidgets.QFrame): """简单交易组件""" signal = QtCore.Signal(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_LME, 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 = '' # 添加交易接口 l = mainEngine.getAllGatewayDetails() gatewayNameList = [d['gatewayName'] for d in l] self.gatewayList.extend(gatewayNameList) self.initUi() self.connectSignal() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(vtText.TRADING) self.setMaximumWidth(400) self.setFrameShape(self.Box) # 设置边框 self.setLineWidth(1) # 左边部分 labelSymbol = QtWidgets.QLabel(vtText.CONTRACT_SYMBOL) labelName = QtWidgets.QLabel(vtText.CONTRACT_NAME) labelDirection = QtWidgets.QLabel(vtText.DIRECTION) labelOffset = QtWidgets.QLabel(vtText.OFFSET) labelPrice = QtWidgets.QLabel(vtText.PRICE) self.checkFixed = QtWidgets.QCheckBox(u'') # 价格固定选择框 labelVolume = QtWidgets.QLabel(vtText.VOLUME) labelPriceType = QtWidgets.QLabel(vtText.PRICE_TYPE) labelExchange = QtWidgets.QLabel(vtText.EXCHANGE) labelCurrency = QtWidgets.QLabel(vtText.CURRENCY) labelProductClass = QtWidgets.QLabel(vtText.PRODUCT_CLASS) labelGateway = QtWidgets.QLabel(vtText.GATEWAY) #中间部分 self.lineSymbol = QtWidgets.QLineEdit() self.lineName = QtWidgets.QLineEdit() self.comboDirection = QtWidgets.QComboBox() self.comboDirection.addItems(self.directionList) self.comboOffset = QtWidgets.QComboBox() self.comboOffset.addItems(self.offsetList) self.spinPrice = QtWidgets.QDoubleSpinBox() self.spinPrice.setDecimals(4) self.spinPrice.setMinimum(0) self.spinPrice.setMaximum(100000) self.spinVolume = QtWidgets.QSpinBox() self.spinVolume.setMinimum(0) self.spinVolume.setMaximum(1000000) self.comboPriceType = QtWidgets.QComboBox() self.comboPriceType.addItems(self.priceTypeList) self.comboExchange = QtWidgets.QComboBox() self.comboExchange.addItems(self.exchangeList) self.comboCurrency = QtWidgets.QComboBox() self.comboCurrency.addItems(self.currencyList) self.comboProductClass = QtWidgets.QComboBox() self.comboProductClass.addItems(self.productClassList) self.comboGateway = QtWidgets.QComboBox() self.comboGateway.addItems(self.gatewayList) gridleft = QtWidgets.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 = QtWidgets.QLabel(vtText.BID_1) labelBid2 = QtWidgets.QLabel(vtText.BID_2) labelBid3 = QtWidgets.QLabel(vtText.BID_3) labelBid4 = QtWidgets.QLabel(vtText.BID_4) labelBid5 = QtWidgets.QLabel(vtText.BID_5) labelAsk1 = QtWidgets.QLabel(vtText.ASK_1) labelAsk2 = QtWidgets.QLabel(vtText.ASK_2) labelAsk3 = QtWidgets.QLabel(vtText.ASK_3) labelAsk4 = QtWidgets.QLabel(vtText.ASK_4) labelAsk5 = QtWidgets.QLabel(vtText.ASK_5) self.labelBidPrice1 = QtWidgets.QLabel() self.labelBidPrice2 = QtWidgets.QLabel() self.labelBidPrice3 = QtWidgets.QLabel() self.labelBidPrice4 = QtWidgets.QLabel() self.labelBidPrice5 = QtWidgets.QLabel() self.labelBidVolume1 = QtWidgets.QLabel() self.labelBidVolume2 = QtWidgets.QLabel() self.labelBidVolume3 = QtWidgets.QLabel() self.labelBidVolume4 = QtWidgets.QLabel() self.labelBidVolume5 = QtWidgets.QLabel() self.labelAskPrice1 = QtWidgets.QLabel() self.labelAskPrice2 = QtWidgets.QLabel() self.labelAskPrice3 = QtWidgets.QLabel() self.labelAskPrice4 = QtWidgets.QLabel() self.labelAskPrice5 = QtWidgets.QLabel() self.labelAskVolume1 = QtWidgets.QLabel() self.labelAskVolume2 = QtWidgets.QLabel() self.labelAskVolume3 = QtWidgets.QLabel() self.labelAskVolume4 = QtWidgets.QLabel() self.labelAskVolume5 = QtWidgets.QLabel() labelLast = QtWidgets.QLabel(vtText.LAST) self.labelLastPrice = QtWidgets.QLabel() self.labelReturn = QtWidgets.QLabel() self.labelLastPrice.setMinimumWidth(60) self.labelReturn.setMinimumWidth(60) gridRight = QtWidgets.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 = QtWidgets.QPushButton(vtText.SEND_ORDER) buttonCancelAll = QtWidgets.QPushButton(vtText.CANCEL_ALL) size = buttonSendOrder.sizeHint() buttonSendOrder.setMinimumHeight(size.height() * 2) # 把按钮高度设为默认两倍 buttonCancelAll.setMinimumHeight(size.height() * 2) # 整合布局 hbox = QtWidgets.QHBoxLayout() hbox.addLayout(gridleft) hbox.addLayout(gridRight) vbox = QtWidgets.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(vtSymbol) 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 AlgoStatusMonitor(QtWidgets.QTableWidget): """""" signalParam = QtCore.Signal(type(Event())) signalVar = QtCore.Signal(type(Event())) #---------------------------------------------------------------------- def __init__(self, algoEngine): """Constructor""" super(AlgoStatusMonitor, self).__init__() self.algoEngine = algoEngine self.eventEngine = algoEngine.eventEngine self.cellDict = {} self.initUi() self.registerEvent() #---------------------------------------------------------------------- def initUi(self): """初始化界面""" labels = [u'', u'名称', u'参数', u'变量'] self.setColumnCount(len(labels)) self.setHorizontalHeaderLabels(labels) self.setRowCount(0) self.verticalHeader().setVisible(False) self.setEditTriggers(self.NoEditTriggers) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signalParam.connect(self.processParamEvent) self.signalVar.connect(self.processVarEvent) self.eventEngine.register(EVENT_ALGO_PARAM, self.signalParam.emit) self.eventEngine.register(EVENT_ALGO_VAR, self.signalVar.emit) #---------------------------------------------------------------------- def addAlgo(self, algoName): """新增算法""" self.insertRow(0) buttonStop = StopButton(self.algoEngine, algoName) cellName = AlgoCell(algoName) cellParam = AlgoCell() cellVar = AlgoCell() self.setCellWidget(0, 0, buttonStop) self.setItem(0, 1, cellName) self.setItem(0, 2, cellParam) self.setItem(0, 3, cellVar) self.cellDict[algoName] = { 'param': cellParam, 'var': cellVar, 'button': buttonStop } #---------------------------------------------------------------------- def processParamEvent(self, event): """""" d = event.dict_['data'] algoName = d.pop('algoName') if algoName not in self.cellDict: self.addAlgo(algoName) l = [] for k, v in d.items(): msg = u'%s:%s' % (k, v) l.append(msg) text = ','.join(l) cell = self.cellDict[algoName]['param'] cell.setText(text) self.resizeColumnsToContents() #---------------------------------------------------------------------- def processVarEvent(self, event): """""" d = event.dict_['data'] algoName = d.pop('algoName') if algoName not in self.cellDict: self.addAlgo(algoName) if 'active' in d: active = d.pop('active') if not active: button = self.cellDict[algoName]['button'] button.disable() l = [] for k, v in d.items(): msg = u'%s:%s' % (k, v) l.append(msg) text = ','.join(l) cell = self.cellDict[algoName]['var'] cell.setText(text) self.resizeColumnsToContents()